数据结构:哈夫曼树【转载自mcgrady https://www.cnblogs.com/mcgrady/p/3329825.html#_label0】

哈夫曼树(Huffman) 又称最优二叉树,是带权路径最短的树。

1.如何建立哈夫曼树

  1. 将所有左,右子树都为空的作为根节点。
  2. 在森林中选出两棵根节点的权值最小的树作为一棵新树的左,右子树,且置新树的附加根节点的权值为其左,右子树上根节点的权值之和。注意,左子树的权值应小于右子树的权值。
  3. 从森林中删除这两棵树,同时把新树加入到森林中。
  4. 重复2,3步骤,直到森林中只有一棵树为止,此树便是哈夫曼树。

下面是构建哈夫曼树的图解过程:


2.哈夫曼编码

    利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。树中从根到每个叶子节点都有一条路径,对路径上的各分支约定指向左子树的分支表示”0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为各个叶子节点对应的字符编码,即是哈夫曼编码。

    就拿上图例子来说:

    A,B,C,D对应的哈夫曼编码分别为:111,10,110,0

用图说明如下:


注:由哈夫曼树求得的编码就是哈夫曼编码。

下面贴上完整代码:
#include <iostream>    
#include<cstdio>    
#include<cstdlib>    
using namespace std;    
typedef struct BTreeNode    
{    
    int  data;    
    struct BTreeNode* left;    
    struct BTreeNode* right;    
}btreenode;    
        
//建立哈夫曼树    
btreenode *CreateHuffman(int a[],int n)  
{    
    int i;    
    btreenode *s[n+1], *ss;    
    for(int i = 0;i<n;i++){    
        s[i] = new btreenode;  //初始化s指针数组,使每个指针元素指向a数组中对应的元素结点    
        s[i]->data = a[i];   //将树拆成森林,每棵树都只有一个根节点    
        s[i]->left = s[i]->right = NULL;    
    }    
      
    for(int i  = 1;i<n;i++){        //进行 n-1次循环建立哈夫曼树    
           int k = -1,t;           //k表示森林中具有最小权值的树根结点的下标,t为次最小的下标    
        for(int j = 0;j<n;j++){   //k初始指向森林中第一棵树,t指向第二棵    
                if(s[j]&&k==-1){    
                   k = j;    
                   continue;    
                }    
                if(s[j]){    
                    t =  j;    
                    break;    
                }    
        }    
            for(int i = t;i<n;i++){   //从当前森林中求出最小权值树和次最小 ;    
            if (s[i]){    
                if(s[i]->data<s[k]->data){        //比最小树小   
                    t = k;    
                    k = i;    
                }    
                else if(s[i]->data<s[t]->data){  //比次小树小   
                    t = i;    
                }    
                else{    
                    ;    
                }    
            }    
        }    
           //由最小权值树和次最小权值树建立一棵新树,ss指向树根结点(以后依次建立)    
           ss  = new btreenode;     //ss = (btreenode *)malloc(sizeof(btreenode))            
           ss->data =   s[k]->data+s[t]->data;    
           ss->left =   s[k];    
           ss->right =  s[t];    
           s[k] =  ss;  //关键点:将ss赋给s[k](k为上述找到的最小树下标,但这是s[k]代表的值已改变,同时把s[t]失效的置空,    
                       //在这里起向下一个判断的作用if(s[j]){t = j;break;}    
           s[t] = NULL;    
    }    
        return ss;   
        free(s);   //释放分配空间    
}     
//求哈夫曼树的带权路径长度        
int WeightPathLength(btreenode* FBT, int len){          //参数len为树的层数   
    if(!FBT){    
            return 0;    
    }    
    else{    
        if(FBT->left ==NULL&&FBT->right ==NULL)//访问到叶子结点    
            return FBT->data*len;    
        else{               //访问到非叶子结点,进行递归调用,返回左右子树的带权路径长度之和,len递增    
            return  WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);//一定要记得加1    
        }    
    }       
}    
   
//哈夫曼编码    
void HuffManCoding(btreenode* FBT, int len){   //参数len为树的层数   
    static int a[20];                        //定义静态数组a,保存每个叶子的编码,数组长度至少是树深度减1   
    if(FBT){                                //访问到叶子结点时输出其保存在数组a中的0和1序列编码    
        if(FBT->left == NULL&&FBT->right == NULL){    
            printf("结点权值为%d的编码:",FBT->data);    
             for(int i = 0;i<len;i++){    
                 printf("%d",a[i]);    
            }    
                printf("\n") ;    
        }    
        else{    //访问到非叶子结点时分别向左右子树递归调用,并把分支上的0、1编码保存到数组    
                //a的对应元素中,向下深入一层时len值增1    
               a[len] = 0;    
               HuffManCoding(FBT->left,len+1);    
               a[len] = 1;    
               HuffManCoding(FBT->right,len+1);    
            }    
    }    
}    
      
int main(){    
    btreenode *s;    
    int n;    
    printf("从键盘输入待构造的哈夫曼树中带权叶子结点数n:");    
    while(true){    
        scanf("%d",&n);    
        if(n>0){    
            break;    
        }    
        else{    
            printf("-------输入不合法,请重新输入!!\n");    
        }    
    }    
    int *a = (int *)malloc(n *sizeof(int));    
    printf("从键盘输入%d个整数作为权值:",n);    
    for (int i = 0; i < n; i++)    
        scanf("%d", &a[i]);    
    s = CreateHuffman(a,n);    
      
    printf("哈夫曼树的带权路径长度:");    
    printf("%d\n", WeightPathLength(s, 0));    
      
    printf("树中每个叶子结点的哈夫曼编码:\n");    
    HuffManCoding(s,0);    
      
    return 0;    
}  

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值