数据结构:赫夫曼树的编码和译码过程

本文详细介绍了赫夫曼树的编码和译码过程,基于严蔚敏版数据结构中的6.6.2赫夫曼编码,讨论了路径长度、带全路径长度、最优二叉树的概念,并阐述了如何构建和存储赫夫曼树,以及实现赫夫曼编码的方法。文章还提到了在实际实现过程中遇到的挑战和解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构:赫夫曼树的编码和译码过程

  • 写在前面的话

这篇文章是根据严蔚敏版的数据结构中6.6.2赫夫曼编码中的部分代码所写,主要是记录一下自己对这些代码的理解,并且在那本书中采用了类c语言写的,在计算机上无法直接实现,我便根据自己的理解对代码进行适当的改写,使其在计算机上可以运行。
(此外,吐槽一句,书上用所谓的类c语言写,确实节省了书的空间,老师讲的时候也更方便,但是自己去实现的时候是真的令人头秃。。。。。)

  • 学前小知识

路径长度:从树的一个结点到另一个结点之间的分支构成这两个节点之间的路径,而路径上的分支数目便称作路径长度。从树根到每一个结点的路径长度之和称为树的路径长度
树的带全路径长度:结点的带全路径长度为该从结点到树根之间的路径长度与结点上的权的乘积,而树中所有叶子结点的带全路径长度之和叫做熟的带全路径长度。
赫夫曼树:又称最优二叉树,是指带全路径长度WPL最小的二叉树。
前缀编码:若要设计长短不等的编码,则必须是任一个字符的编码都不是另一个字符编码的前缀。而赫夫曼编码便是总长度最短的前缀编码。
图6.25

  • 赫夫曼树的编码

当给到我们一组有权重的数时:

weightparentlchildrchild
15000
229000
37000
48000
514000
623000
73000
811000
9-000
10-000
11-000
12-000
13-000
14-000
15-000
                 ( a)HT的初态

首先便是将其构成赫夫曼树。其方法如图所示:

由方法可将上面的数组填充完整:

weightparentlchildrchild
15900
2291400
371000
481000
5141200
6231300
73900
8111100
981117
10151234
11191389
122914510
134215611
145815212
1510001314
               (b)HT的终态

由定义可知,赫夫曼树中没有度为1的结点,则一棵有n个子叶结点的赫夫曼树共有2n-1个结点
而根据图6.25可知若要求赫夫曼编码,则需从叶子结点出发走出一条从叶子到根的路径。所以在定义赫夫曼树和赫夫曼编码的存储结构时,不仅要考虑一个结点的权重,还需考虑该结点的双亲和孩子结点的位置。

所以其顺序存储结构可以这样定义

typedef struct {
	unsigned int weight;
	unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree; //动态分配数组存储赫夫曼树 

typedef char * * HuffmanCode;//动态分配数组存储赫夫曼编码表 

其中weight表示权重,parent表示双亲,lchild表示左孩子,rchild表示右孩子。
接下来便是赫夫曼的编码:

//---------------基本操作的算法描述----------------
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n){
	//w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC
	
	//------构造赫夫曼树---- 
{
	if(n<=1){
		return ERROR;
	} 
	m = 2 * n - 1;
	HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用
	for(int i = 1;i <= n;i++){	
	    //构造(a)有数据部分
		HT[i].weight = w[i];
		HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
	}
	for(int i = n+1;i <= m;i++){
	    //构造(a)没有有数据部分
		HT[i].weight = HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
	}
	for(int i = n+1;i <= m;i++){ //构建赫夫曼树 
		//在HT[1...i-1]选择parent为0且weight最小的两个节点,其序号分别为s1和s2.
		//构造(b)
		Select (HT,i-1,s1,s2);//自定义select函数,用于查找权重最小的两个结点
		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight; 
	}
}
	//------从叶子到根逆向求每个字符的赫夫曼编码----
	HC = (HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
	char cd = (char*)malloc(n*sizeof(char)); //分配求编码的工作空间
	cd[n-1] = "\0";  //编码结束符
	for(int i = 1;i <= n;i++){  //逐个字符求赫夫曼编码 
		int start = n - 1;   //编码结束符位置
		for(char c = i,f = HT[i].parent;f != 0;c = f,f = HT[f].parent){
			//从叶子到根逆向求编码
			if(HT[f].lchild == c){
				sd[--start] == "0";
			} else{
				sd[--start] == "1";
			}
		} 	
		HC[i] = (char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
		strcpy(HC[i],&sd[start]);//自定义strcpy函数,用于从cd复制编码(串)到HC 	
	}
	free(cd);//释放空间 
}

至此,赫夫曼编码完成。
未完待续… … …

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值