【数据结构】设计哈夫曼树,实现哈夫曼编码
假设有一段电文由字符集 {A, B, C, D, E, F, G, H} 中的字符组成,各字符在电文中出现的频率由对应次数集 {5,29,7,8,14,23,3,11} 中的数字表示,请设计各字符的哈夫曼编码。//如果 min1
实验5 二叉树的基本操作
一、实验目的
熟练应用二叉链表存储结构,实现二叉树的构建,遍历等操作。
二、实验软硬件要求
硬件:一台安装了windows操作系统的计算机。
软件:C语言编程工具
- 实验内容(需写出源程序)
【问题描述】
假设有一段电文由字符集 {A, B, C, D, E, F, G, H} 中的字符组成,各字符在电文中出现的频率由对应次数集 {5,29,7,8,14,23,3,11} 中的数字表示,请设计各字符的哈夫曼编码。
【基本要求】
应包含以下几方面的功能:
- 设计哈夫曼树。具体构造方法如下:以字符集{A, B, C, D, E, F, G, H} 中的字符作为叶子结点,以各字符在次数集 {5,29,7,8,14,23,3,11} 中对应的次数作为各叶子结点的权值构造一棵哈夫曼树。
- 设计哈夫曼编码。按照构造出来的哈夫曼树,规定哈夫曼树的左分支为0,右分支为1,则从根结点到每个叶子结点所经过的分支对应的0和1组成的序列便为该结点对应字符的哈夫曼编码。
- 依次求出每个字符的哈夫曼编码并输出。
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
typedef struct{
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码表
void CreateHuffmanTree(HuffmanTree &HT,int n); //构造哈夫曼树
void Select(HuffmanTree HT,int end,int *s1,int *s2); //select函数
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n); //哈夫曼编码
void CreateHuffmanTree(HuffmanTree& HT,int n){
//构造哈夫曼树
if(n<=1) return;
int m=2*n-1;
HT=new HTNode[m+1]; //0单元号未用,下标从1开始
for(int i=1;i<=m;i++){ //初始化,将下标1~m号结点的双亲,左孩子,右孩子置为0
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(int i=1;i<=n;i++){
cin >> HT[i].weight; //输入前n个结点的权值
}
// 初始化结束,下面创开始创建哈夫曼树
//---------------------------------------------------------
for(int i=n+1;i<=m;i++){
/* 在select函数中,在前n个结点中通过n-1次的选择两个权值较小的结点,
进行合并,删除 来创建哈夫曼树 */
int s1=0,s2=0;
Select(HT,i-1,&s1,&s2);
/* 在select中挑选出两个权值较小的结点,且s1<s2;
合并成一个新的结点,新的结点的结点号为i ,此时s1和s2结点的双亲结点即为i */
HT[s1].parent=i; HT[s2].parent=i;
HT[i].lchild=s1;//将s1和s2分别作为结点i的左右孩子
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;//结点i的权值为左右孩子之和
}
}
//Select函数
void Select(HuffmanTree HT,int end,int *s1,int *s2){
int min1,min2;//min1存放较小的,min2存放第二小的,min1<min2
//先挑选出一个双亲结点为0的结点 ,如果双亲节点不为0说明这个结点已经在生成新节点中被使用过
int i=1;
while(HT[i].parent!=0&&i<=end){
i++;
}
//将第一次挑选出来的结点的权值先赋值给min1,然后i加一,挑选第二个双亲结点为0的结点
min1=HT[i].weight;
*s1=i;
i++;
// 挑选第二个双亲结点为0的结点
while(HT[i].parent!=0&&i<=end){
i++;
}
/*对挑选出来的第一个结点第二个结点再进行权值的比较,如果第二次挑选出来的HT[i].weight
小于min1的值,则先将min1的值付给min2,再将HT[i].weight赋给min1; 如果第二次挑选出来的
HT[i].weight大于min1的值,则将HT[i].weight赋给min2
*/
if(HT[i].weight<min1){
min2=min1;
*s2=*s1;
min1=HT[i].weight;
*s1=i;
}else{
min2=HT[i].weight;
*s2=i;
}
//对余下的结点进行遍历
for(int j=i+1;j<=end;j++){
if(HT[j].parent!=0) continue;
if(HT[j].weight<min1){
min2=min1;
min1=HT[j].weight;
*s2=*s1;
*s1=j;
}
//如果 min1<=HT[i].weight<=min2,则 将HT[j].weight的值赋给min2
else if(HT[j].weight>=min1&&HT[j].weight<min2){
min2=HT[j].weight;
*s2=j;
}
}
}
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
//得到哈夫曼编码
int start,c,f;
HC=new char*[n+1]; //下表从1开始,分配存储n个字符编码的编码表空间
char* cd=new char[n]; //临时存放每个字符编码的动态数组空间
cd[n-1]='\0'; //编码结束符
for(int i=1;i<=n;i++){ //逐个字符求哈夫曼编码
start=n-1; //start开始指向最后,即编码结束符的位置
c=i;f=HT[i].parent; //f指向结点c的双亲结点,
while(f!=0){
start--; //回溯一次,start位置向前指一个位置
if(HT[f].lchild==c) cd[start]='0'; //如果结点c是f的左孩子,则生成代码0
else cd[start]='1'; //如果结点c是f的右孩子,则生成代码0
c=f;f=HT[f].parent; //继续向上回溯,直至 f的双亲为0回溯结束
}
/* 注意:1.for循环是为了逐个字符求哈夫曼编码;while循环是为了对所求字符进行回溯,直至双亲为0
2.结点的双亲,左孩子,右孩子指向的全都是结点i,并不是结点的权值,这一点很容易混淆
*/
HC[i]=new char[n-start]; // 为第i个字符进行编码
strcpy(HC[i],&cd[start]); //为第i个字符编码分配空间
}
delete cd; //释放临时空间
}
int main(){
HuffmanTree HT;
HuffmanCode HC;
cout<<"个字符集的频率为:"<<endl;
CreateHuffmanTree(HT,5);
CreateHuffmanCode(HT,HC,8);
cout << "字符集编号" << endl;
for (int i = 1; i <= 8; i++) {
cout << HC[i] << endl;
}
return 0;}
【扩展功能】
从键盘上分别输入哈夫曼编码字符的个数以及每个字符对应的权值,程序执行中请一步一步依次显示出哈夫曼树的构造过程,最后输出每个权值对应的哈夫曼编码。
四、实验结果(写出运行程序后的结果截图)
我的其他专栏:
关注我了解更多

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)