1. 实验目的
(1)能设计huffman树存储结构,并根据n个权值构建huffman树;
(2)能根据huffman树进行huffman编码;
2. 实验内容
(1)第一关:根据给定权值,动态创建huffman树;
(2)第二关:给出各权值对应的huffman编码;
(3)选做关:给定一串编码,根据huffman树进行译码。
任务描述
本关任务:编写能对给定n个叶子结点,构建哈夫曼树,给出每个叶子结点对应编码的程序。
相关知识
哈夫曼编码和译码的基本原理
- 首先要构造一棵哈夫曼树。哈夫曼树的结点结构包括权值,双亲,左右孩子;假如由n个字符来构造一棵哈夫曼树,则共有结点2n-1个;在构造前,先初始化,初始化操作是把双亲,左右孩子的下标值都赋为0;然后依次输入每个结点的权值。
- 第二步是通过n-1次循环,每次先找输入的权值中最小的两个结点,把这两个结点的权值相加赋给一个新结点,并且这个新结点的左孩子是权值最小的结点,右孩子是权值第二小的结点;鉴于上述找到的结点都是双亲为0的结点,为了下次能正确寻找到剩下结点中权值最小的两个结点,每次循环要把找的权值最小的两个结点的双亲赋值不为0(i)。就这样通过n-1循环下、操作,创建了一棵哈夫曼树,其中,前n个结点是叶子(输入的字符结点)后n-1个是度为2的结点。
- 编码的思想是逆序编码,从叶子结点出发,向上回溯,如果该结点是回溯到上一个结点的左孩子,则在记录编码的数组里存“0”,否则存“1”,注意是倒着存;直到遇到根结点(结点双亲为0),每一次循环编码到根结点,把编码存在编码表中,然后开始编码下一个字符(叶子)。
- 译码的思想是循环读入一串哈夫曼序列,读到“0”从根结点的左孩子继续读,读到“1”从右孩子继续,如果读到一个结点的左孩子和右孩子是否都为0,如果是说明已经读到了一个叶子(字符),翻译一个字符成功,把该叶子结点代表的字符存在一个存储翻译字符的数组中,然后继续从根结点开始读,直到读完这串哈夫曼序列,遇到结束符便退出翻译循环。(译码内容为选做内容)
任务描述
在右侧编辑器中补充代码,编写能对给定n个叶子结点,构建哈夫曼树,给出每个叶子结点对应编码的程序。
测试说明
平台会对你编写的代码进行测试:
测试输入:5 2 7 4 5 19
预期输出:(对哈夫曼树按中序遍历输出对应叶子的哈夫曼编码)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
struct InputData* CreateHuffData(int size);
void OutputInputData(struct InputData*input_data,int size);
void CodingHuff(struct InputData*input_data,int size);
void PrintHuffCode(struct InputData*input_data,int size);
struct InputData{
int weight;
int value;
int parent,lchild,rchild;
};
void CodingHuff(struct InputData*input_data,int size){
int cur_size=size;
for(int i=0;i<size-1;i++){
int max=input_data[0].weight;
for(int j=0;j<cur_size;j++){
if(input_data[j].weight>max){
max=input_data[j].weight;
}
}
int min_1=max+1,min_2=max+1;
int x_1=-1,x_2=-1;
for(int j=0;j<cur_size;j++){
if(input_data[j].weight<min_1&&input_data[j].parent==-1){
min_2=min_1;
x_2=x_1;
min_1=input_data[j].weight;
x_1=j;
}else if(input_data[j].weight<min_2&&input_data[j].parent==-1){//j位置只比min_2要小
min_2=input_data[j].weight;
x_2=j;
}
}
input_data[cur_size].lchild=x_1;
input_data[cur_size].rchild=x_2;
input_data[cur_size].weight=min_1+min_2;
input_data[x_1].parent=cur_size;
input_data[x_2].parent=cur_size;
cur_size+=1;
}
}
struct InputData* CreateHuffData(int size){
struct InputData*input_data=(struct InputData*)malloc(sizeof(struct InputData)*(size*2-1));
for(int i=0;i<size*2-1;i++){
input_data[i].parent=-1;
input_data[i].lchild=-1;
input_data[i].rchild=-1;
input_data[i].weight=0;
input_data[i].value=0;
}
for(int i=0;i<size;i++){
cin>>input_data[i].weight;
input_data[i].value=input_data[i].weight;
}
CodingHuff(input_data,size);
return input_data;
};
void PrintHuffCode(struct InputData*input_data,int root,vector<char>&huffcode){
if(input_data[root].lchild!=-1&&input_data[root].rchild!=-1){
huffcode.push_back('0');
PrintHuffCode(input_data,input_data[root].lchild,huffcode);
huffcode.push_back('1');
PrintHuffCode(input_data,input_data[root].rchild,huffcode);
huffcode.erase(huffcode.end()-1);
}else{
cout<<input_data[root].weight<<" ";
for(int i=0;i<huffcode.size();i++){
cout<<huffcode[i];
}
cout<<endl;
huffcode.erase(huffcode.end()-1);
}
return;
}
void OutputInputData(struct InputData*input_data,int size){
vector<char>huffcode;
PrintHuffCode(input_data,size*2-2,huffcode);
huffcode.clear();
}
int main(int argc,char**argv)
{ printf("第一个数字为结点数,后面的数字代表权值:");
int size;
cin>>size;
struct InputData*huff_data=CreateHuffData(size);
OutputInputData(huff_data,size);
free(huff_data);
return 0;
}