哈夫曼树与哈弗曼编码

                                   哈夫曼树与哈夫曼编码

  • 相关概念

  1.    路径:      树中一个结点到另一个结点之间的分支序列。
  2. 路径长度:  路径上分支的条数
  3. 结点的权:   给结点赋予的数值
  4. 带权路径长度:结点的权值与该结点到树根间路径长度的乘积
  5. 树的带权路径长度:树种所有叶结点的带权路径长度之和,记为:  WPL=(W1*L1 + W2*L2 + W3*L3 +......+ Wn*Ln)
  6. 哈夫曼树:(最优二叉树)是指在叶子结点个数n和叶子权值Wi确定的情况下,树的带权路径长度值WPL为最小的二叉树。

【注】n为叶结点个数  ;  Wi为第i个叶结点的权值; Li为第i个叶结点的带权路径长度

  • 引例

【注】哈夫曼树依据最优二叉树的特点(权值越大,离根越近~)给出的构造方法,因此最优二叉树又称哈夫曼树

  • 哈夫曼树的建立

  1. 初始化: 按给定n个权值{W1,W2,W3.......Wn},构造二叉树的集合F={T1,T2,T3......Tn},                                                 其中, ,每棵二叉树只含一个权值为Wi的根节点,左右子树为空。
  2. 在F中选取根节点权值最小的两棵二叉树,分别作为左右子树 并 将其和作为根节点 构造新的二叉树
  3. 在F中删除所选中的两棵小树,将新生成的二叉树插入F
  4. 重复2.和3.两步,直到F中只剩一棵树为止。

【注例】:已知权值W={5,8,4,9,3,4},构造哈夫曼树

       步骤:

               1.初始化:

               2.建新树:

               3.删结点:

4:直到F中只剩一个结点为止,结束:

  • 哈夫曼树特点:

                      1. 哈夫曼树没有度为1的结点:n1==0;

                      2. n 个叶子结点的哈夫曼树共有 2n-1 个结点。(N=n0+n1+n2 与 n1=0与n2=n0-1可证)

                      3.哈夫曼树的任意非叶结点的左右子树交换顺序后仍是哈夫曼树

                      4. 对于同一组权值{W1,W2....Wn},当有重复权值时,存在不同构的哈夫曼树

  • 哈夫曼算法实现:

      【注】在哈夫曼树进行编码译码时,要用到双亲及孩子节点的信息,所以一般我们采用静态三叉链表来存储哈夫曼树.

-----------------------@定义存储结构:--------------

#define N 
#define M 2*N+1

typedef struct node{
int weight;//权值
int parent;//父节点
int Lchild;//左孩子
int Rchild;//右孩子
}Hffuman,HuffmanT[M+1];

      ------------------@哈夫曼树的构建过程-----------------------

  1. 初始化: (下标为1--->n 的结点) 叶子节点赋权值,父子游标均初始化为0                                                                                                (下标为n+1 --->2n-1 的结点) 所有域都赋为0;
  2. i=n+1;i<2n-1; i++重复执行以下两步构建哈夫曼树:                                                                                                                                      a. 选择最小两棵最小树 在下标为1--->n选择根结点(父域为0的结点)权值最小的两棵二叉树                   b.                  b.  构造新树:  计算新结点i的权值,置新节点的左右子域以及2棵子树的父域 (隐含了删除节点操作)                 

   ---------------- @代码实现:---------------------                             

void CreateHFM(Huffman HT,int w[],int n){
        for(i=1;i<=n;i++){
         HuffmanT[i]={w[i],0,0,0};
         }//叶结点初始化
        for(i=n+1;i<=2*n-1;i++){
         HuffmanT[i]={0,0,0,0};
         } //分支节点初始化
        for(i=n+1;i<2*n-1;i++){
           select(HuffmanT[i],&min1,&min2);
            HuffmanT[i].weight=HuffmanT[min1].weight+HuffmanT[min2].weight;
            HuffmanT[i].Lchild=HuffmanT[min1];
            HuffmanT[i].Rchild=HuffmanT[min2];
            HuffmanT[min1].parent=HuffmanT[i];
            HuffmanT[min2].parent=HuffmanT[i];
         }
}
  • 哈弗曼编译码:

        【定义】: 规定哈夫曼树中的左分支为0,右分支为1,则从根节点到每个叶结点所经过的分支对应的0和1组成的序列便为该节点对应字符的编码。因此哈夫曼编码属于二进制编码。

        【特点】:   1.权值越大的字符编码越短,反之越长。

                            2.在一组哈弗曼编码中,不可能出现一个字符的哈夫曼编码是一个字符哈夫曼编码的前缀

       【结论】:1.哈夫曼编码是前缀编码  2.哈夫曼编码是最优编码

       【编码】:由哈夫曼树构造哈夫曼编码,需要从叶子出发,走一遍从叶子到根的路径,编码是由后往前生成,每经过一条分支,就得到   一位哈夫曼编码值,其中,节点为双亲的左孩子时当前编码为0,为双亲的右孩子时当前编码值为1

       【译码】:需要从根节点出发走一遍从根到叶子的路径,当前编码为0时走向左孩子,编码为1 时走向右孩子,当走到叶子节点时就完成了对一个字符的编码。

  • 哈弗曼编码的算法实现:

        【注】在算法中,生成的编码暂存在字符串cd中,一个叶子节点的编码构造完成,再将其转存到最终的字符数组中。

typedef char* Huffmancode[n+1];
char *cd;  //从叶子到根逐个保存哈夫曼编码 
int start;  
CreateHuffmanCode(HuffmanT HT,HuffmanCode hc,int n){
	cd=(char*)malloc(n*sizeof(char));
	cd[n-1]='\0';   //从右向左逐位求编码,先置串尾结束符 
    for(i=1;i<=n;i++){
    	start=n-1; 
		  //child指示当前节点,parent为其双亲 
		child=i;  
		parent=HT[child].parent;
    	while(parent!=0){ //只要没到根节点就一直循环 
    		if(HT[parent].Lchild==child)
    		{
    			cd[start--]='0'; //是双亲的左孩子就编码为0; 
			}
			else{
				cd[start--]='1';  //是双亲的右孩子就编码为1;  
			}
			//继续回溯 
			child=parent;   //child更新为父亲 
			parent=HT[parent].parent;   //parent变为爷爷 
		}
		hc[i]=(char*)malloc((n-start)*sizeof(char));
		strcpy(hc[i],&cd[start]);
    }
}
  • 正经致谢:

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值