数据结构之树(八)——哈夫曼编码

哈夫曼编码思想

  • 问题描述: 在远程通讯中,要将待传送的字符先编码再解码,编码过程就是将字符转换成由二进制组成的字符串。假设传送的字符为:ABACCDA,如果我们采用等长的编码方式,若每个字符的编码为 A : 00 B : 01 C : 10 D : 11 A:00\\B:01\\C:10\\D:11 A:00B:01C:10D:11则待传送字符的编码为00010010101100。这种编码方式的一个很明显的缺点就是浪费空间。如果这里要传送的字符很多,如果用ASCII码表示,每一个ASCII码占8位,那么就会很浪费存储空间。
    所以,若将编码设计成长度不等的二进制编码,即让待传送字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。
    假设要传送的字符为:ABACCDA,不等长的编码为: A : 0 B : 00 C : 1 D : 01 A:0\\B:00\\C:1\\D:01 A:0B:00C:1D:01,则编码为000011010。但这种编码很容易产生重码问题,如上述的编码在解码的时候,前四位"0000"可以解为AAAA,也可以解为ABA或BB。
    在设计不等长编码的时候,关键在于必须使任一字符的编码都不是另一个字符编码的前缀。——在这种编码叫前缀编码

  • 什么样的前缀码能使得电文总长最短?——哈夫曼编码
    方法如下:

    • 统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)。
    • 利用哈夫曼树的特点:权值越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
    • 在哈夫曼树的每个分支上标上0或1:
      • 结点的左分支标0,右分支标1
      • 把从根到每个叶子结点的路径上的标号连接起来,作为该叶子代表的字符的编码。
  • 例子: 要传输的字符集 D = { C , A , S , T , ; } D=\{C,A,S,T,;\} D={C,A,S,T,;},字符出现的频率 w = { 2 , 4 , 2 , 3 , 3 } w=\{2,4,2,3,3\} w={2,4,2,3,3}
    首先,构造哈夫曼树,
    在这里插入图片描述
    接着,进行01标记(左0右1),
    在这里插入图片描述
    从根结点到每个叶结点的路径的01序列,就是每个字符的哈夫曼编码,即, T : 00 ; : 01 A : 10 C : 110 S : 111 T:00\\;:01\\A:10\\C:110\\S:111 T:00;:01A:10C:110S:111

  • 哈夫曼编码的性质:

    • 哈夫曼编码是前缀码。
      因为没有一片树叶是另一片树叶的祖先,所以每个叶结点的编码就不可能是其它叶结点编码的前缀。
    • 哈夫曼编码是最优前缀码。
      因为哈夫曼树的带权路径长度最短,故字符编码的总长度最短。
  • 例子: 设组成电文的字符集D及其概率分布W为: D = { A , B , C , D , E , F , G } W = { 0.40 , 0.30 , 0.15 , 0.05 , 0.04 , 0.03 , 0.03 } D=\{A,B,C,D,E,F,G\}\\W=\{0.40,0.30,0.15,0.05,0.04,0.03,0.03\} D={A,B,C,D,E,F,G}W={0.40,0.30,0.15,0.05,0.04,0.03,0.03}设计哈夫曼编码。
    在这里插入图片描述



哈夫曼编码具体实现

  • 例子: 以上例为例,说明哈夫曼编码的实现过程。在上一篇博客中,已经说明用一维结构数组存储每个结点,即如下图,
    在这里插入图片描述
    以找结点G的编码为例,首先找G的双亲结点,为结点8,在看结点8的左右孩子域,发现结点G所在的结点7为其左孩子,所以,结点G的最后一位是0;接着再看结点8的双亲,是结点10,结点8在结点10的左孩子域,所以倒数第二位编码是0;接着再看结点10的双亲,是结点11,结点10在结点11的左孩子域,所以倒数第三位编码是0;接着再看结点11的双亲,是结点12,结点11在结点12的左孩子域,所以倒数第四位编码是0;接着再看结点12的双亲,是结点13,结点12在结点13的右孩子域。所以倒数第五位编码是1;最后,当看到结点13的双亲结点为0时(结点13为根结点),结束;得出结点G的编码是10000

  • 具体实现:
    定义一个字符串数组,用于存放每个结点的哈夫曼编码,
    在这里插入图片描述
    再用一个字符数组存放每个结点回溯时暂时得到的哈夫曼编码,数组长度实际上n-1个就够了,但在这里定义数组长度为n,最后一位存"\0"。
    在这里插入图片描述

  • 算法描述:

void CreateHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
{
	HC = new char *[n + 1];               //分配n个字符编码的头指针矢量
	cd = new char[n];                     //分配临时存放编码的动态数组空间
	cd[n - 1] = '\0';                     //编码结束符
	for (i = 1; i <= n; ++i)              //逐个字符求哈夫曼编码
	{
		start = n - 1;
		c = i;
		f = HT[i].parent;
		while (f != 0)                    //从叶子结点开始向上回溯,直至根结点
		{ 
			--start;                      //回溯一次start向前指一个位置
			if (HT[f].lchild == c)        //结点c是f的左孩子,则生成编码0
				cd[start] = '0';
			else                          //结点c是f的右孩子,则生成编码1
				cd[start] = '1';
			c = f;
			f = HT[f].parent;             //继续向上回溯
		}                                 //求出第i个字符的编码
		HC[i] = new char[n - start];      //为第i个字符串编码分配空间
		strcpy(HC[i], &cd[start]);        //将求得的编码从临时空间cd复制到HC的当前行中
	}
	delete cd;                            //释放临时空间
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值