哈夫曼树的构造算法以及计算加权路径长度WPL

Dear 丶 2023-05-21 11:54 76阅读 0赞

哈夫曼树的构造算法

算法思路

有W1,W2… …W1 一共n个权值的结点,把每个结点看作一棵树。

  1. 从n个结点中找出权值最小的两个结点,创建一个新结点,权值为二者的和。
  2. 两个结点分别为新结点的左右孩子(左小右大,左大右小都可以)。
  3. 将两个结点从森林中删除,新结点加入其中
  4. 重复1 ~ 3步,直至只剩一棵树,这棵树便是哈夫曼树。

如何用一个数组来构建一棵树?如 arr = {3,4,2,5,7,2}

可以发现第二步中每次是取树中权值最小的两个,用完以后需要删除,并且新结点加入,新结点的大小未知。

满足动态的删增需求,元素之间是简单的线性关系,所以采用链表的存储结构来存储这些树的结点。

这个链表应该时刻保持有序,每次从表中删除最小的两个结点,时间复杂度是O(1),插入新结点的时候要保持链表有序,时间复杂度是O(n);

下面是C语言算法实现:

  1. //链表结点的结构
  2. typedef struct LinkNode{
  3. SearchTree tree;
  4. struct LinkNode * next;
  5. }LinkNode,*LinkList;
  6. //链表(含头结点)的删除方法,每次删除第一个非头结点的结点
  7. LinkNode * pop(LinkList list){
  8. LinkNode * result = nullptr;
  9. if(list->next)result = list->next;
  10. list->next = list->next->next;
  11. return result;
  12. }
  13. //插入的时候保持链表有序
  14. void * push(LinkList list,SearchTree tree){
  15. auto node = (LinkList)malloc(sizeof(LinkList));
  16. node->tree = tree;
  17. while(list->next){
  18. if(list->next->tree->data >= tree->data)break;
  19. else list = list->next;
  20. }
  21. node->next = list->next;
  22. list->next = node;
  23. }
  24. //生成哈夫曼树的主函数
  25. SearchNode * GreateHFM(const int * arr,int length){
  26. if(length < 1)return nullptr;//数组长度小于1返回空
  27. auto head = (LinkList)malloc(sizeof(LinkNode));//首先创建头结点
  28. head->next = nullptr;
  29. //遍历权值数组,每一个权值对应一个树结点
  30. for (int i = 0; i < length;i++) {
  31. auto tree = (SearchTree)malloc(sizeof(SearchNode));
  32. tree->data = arr[i];
  33. tree->left = tree->right = nullptr;
  34. push(head,tree);//将其加入到有序链表中
  35. }
  36. //n个结点要循环n - 1次
  37. for(int i = 0;i < length - 1;i++){
  38. LinkNode* tree1 = pop(head);
  39. LinkNode* tree2 = pop(head); //弹出了两个最小的结点
  40. auto * father = (SearchTree)malloc(sizeof(SearchNode)); //二者权值之和的结点
  41. father->data = tree1->tree->data + tree2->tree->data;
  42. father->left = tree1->tree;
  43. father->right = tree2->tree;
  44. push(head,father);//将新结点加入到链表中
  45. }
  46. return head->next->tree;//最后将哈夫曼树返回
  47. }

计算哈夫曼树的带权路径长度

如何计算每个叶结点到根结点的路径长度?只有知道路径长度才能计算带权路径长度

方法有很多,这里采用后序遍历的非递归算法来实现。
如果不懂后序遍历的非递归算法,可以参考下面这个博文的对应内容:
二叉树遍历全面总结(先,中,后序,层序,递归及非递归版本,如何利用遍历方法解决实际问题)

  1. int compute_WPL(SearchTree tree){
  2. if(tree == nullptr)return 0;//当树为空的时候返回0
  3. stack<SearchTree >helper;//辅助栈,用于后序遍历
  4. int wpl = 0; //最后要返回的带权路径长度之和
  5. int height = 0;//表示当前结点对应的层级
  6. SearchNode * pre = nullptr;
  7. while(tree || !helper.empty()){
  8. if(tree){
  9. helper.push(tree);
  10. height++;
  11. tree = tree->left;
  12. }else{
  13. tree = helper.top();
  14. if(tree->right && (!pre || pre != tree->right)){
  15. tree = tree->right;
  16. }else{
  17. if(!tree->right)wpl += (height - 1) * tree->data;//只有是叶结点的时候才计算带权路径长度
  18. helper.pop();
  19. height--;
  20. pre = tree;
  21. tree = nullptr;
  22. }
  23. }
  24. }
  25. return wpl;
  26. }

发表评论

表情:
评论列表 (有 0 条评论,76人围观)

还没有评论,来说两句吧...

相关阅读

    相关 构造(C语言)

    哈夫曼树又称最优树,即带权路径长度最小的二叉树。 构造过程是典型的贪心算法,即每一步都求取最优情况使整体情况也达到最优。所以构造哈夫曼树时,应该让权重小的结点放在靠下的位置让