实验题目:Huffman编码、译码
实验内容
- 将字符出现的频率作为结点的权值,构造一棵Huffman树,输出Huffman树各节点的值,输出各字符对应的Huffman编码。
- 假设字符集
c
,字符频率集w
如下:char c[]={a,b,c,d,e,f}; int w[]={8,3,4,6,5,5};
- 假设字符集
- (选做) 输入待译码的信号,能正确进行译码。
- 如:对输入信号
10010001101111000
能译出正确的字符序列abdefad
- 如:对输入信号
实验要求
-
构造一棵Huffman树
- 新建项目,项目里新建文件“huffmanTree.h”,在“huffmanTree.h”里定义Huffman树的结点类型如下:
typedef struct { char data; //节点值 double weight ; //权值域 int parent , lchild , rchild ; } HTNode ;
- 在“huffmanTree.h”里实现构造Huffman树的运算
CreateHT
- “main.cpp”里,由给定字符集
c
和字符频率集w
定义初始数组变量如下:int w[]={8,3,4,6,5,5}; char c[]={'a','b','c','d','e','f'}; int nums=6;
- “main.cpp”里,定义Huffman结点数组如下:
HTNode HT[2*nums-1]; //哈夫曼树结点数组
- “main.cpp”里,赋初始
data
和weight
至HT
数组如下:for(int i=0;i<nums;i++) { HT[i].weight=w[i]; HT[i].data=c[i]; }
- “main.cpp”里,调用构建Huffman树如下:
CreateHT(HT,nums); //创建哈夫曼树
- “main.cpp”里,显示Huffman树中各结点的值
- (请补充以下代码)
- 新建项目,项目里新建文件“huffmanTree.h”,在“huffmanTree.h”里定义Huffman树的结点类型如下:
-
求Huffman编码
- 在“huffmanTree.h”里定义哈夫曼编码结点类型如下:
#define N 10 //N的值大于n(想想为什么?) typedef struct { char cd[N]; //存放当前节点的哈夫曼码 int start; //存放哈夫曼码在cd中的起始位置 } HCode; //哈夫曼编码结点类型
- 在“huffmanTree.h”里实现Huffman树编码运算
CreateHCode
- “main.cpp”里,定义存放Huffman编码数组如下:
HCode HC[nums];
- “main.cpp”里,调用Huffman编码运算如下:
CreateHCode(HT, HC, nums);
- “main.cpp”里,显示各个字符对应的编码:
- (请补充以下代码)
- 在“huffmanTree.h”里定义哈夫曼编码结点类型如下:
-
对输入信号进行译码
- 在“huffmanTree.h”里实现Huffman译码运算
EnHCode
- “main.cpp”里,定义要译码的信号
code
及显示code
信息如下:char code[]="10010001101111000"; //abdefad printf("\n\n待译码信号为:"); for(int k=0;code[k]!='\0';k++) { printf("%c",code[k]); }
- “main.cpp”里,定义字符数组
DeCode
用于保存译码后的字符串如下:char DeCode[20]; //保存译码后的字符串
- “main.cpp”里,如果成功译码显示译码后的字符串。
if( EnHCode(code,HT,DeCode,nums) ) { printf("\n\n成功译码!"); printf("\n译码后:"); int i=0; while(DeCode[i] != '\0') { printf("%c",DeCode[i]); i++; } } else { printf("\n\n代码错误!"); }
- 在“huffmanTree.h”里实现Huffman译码运算
实验过程记录
- 运行结果如图:
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#define N 10
typedef struct
{
char data; //节点值
double weight; //权值域
int parent, lchild, rchild;
} HTNode;
typedef struct
{
char cd[N]; //存放当前节点的哈夫曼码
int start; //存放哈夫曼码在cd中的起始位置
}HCode; //哈夫曼编码结点类型
void CreateHT(HTNode ht[],int n)
{ int i,j,k,lnode,rnode; float min1,min2;
for (i=0;i<2*n-1;i++)
ht[i].parent = ht[i].lchild = ht[i].rchild = -1;
for (i=n; i<2*n-1; i++)
{ min1 = min2 = 32767;
lnode = rnode = -1;
for (k=0; k<=i-1; k++)
if (ht[k].parent == -1) //未构造二叉树的结点中查找
{ if (ht[k].weight < min1) //找最小的weight
{ min2 = min1;
rnode = lnode;
min1 = ht[k].weight;
lnode = k;
} else if (ht[k].weight < min2) //找次小的weight
{ min2 = ht[k].weight;
rnode = k;
}
} //if
ht[lnode].parent = i;
ht[rnode].parent = i;
ht[i].weight = ht[lnode].weight + ht[rnode].weight;
ht[i].lchild = lnode;
ht[i].rchild = rnode;
}
}
void initHT(HTNode ht[], int nums, int w[], char c[]) {
for(int i=0;i<nums;i++)
{
ht[i].weight=w[i];
ht[i].data=c[i];
}
}
void CreateHCode(HTNode ht[], HCode hcd[], int n)
{ int i,f,c; HCode hc;
for (i=0; i<n; i++)
{ hc.start = n;
c = i ;
f = ht[i].parent;
while ( f != -1)
{ if( ht[f].lchild == c )
hc.cd[hc.start--] = '0';
else
hc.cd[hc.start--] = '1';
c=f; f=ht[f].parent;
}
hc.start++;
hcd[i]=hc;
}
}
void printHT(HTNode ht[], int nums, char c[]) {
for(int i=0;i<2*nums-1;i++)
{
if(i<=5){
ht[i].data=c[i];
}else
ht[i].data='\0';
printf("Node %-2d: data=%-2c, weight=%-2.0f, parent=%-2d, lchild=%-2d, rchild=%-2d\n", i, ht[i].data, ht[i].weight, ht[i].parent, ht[i].lchild, ht[i].rchild);
}
}
void printHCode(HTNode ht[], HCode hcd[], int nums) {
for(int i=0;i<nums;i++)
{
printf("Node %-2d: data=%-2c, Code=%s\n", i, ht[i].data,&hcd[i].cd[hcd[i].start]);
}
}
int EnHCode(char code[], HTNode ht[], char DeCode[], int nums) {
int i = 0, j = 0;
while(code[i] != '\0') {
if(code[i] == '0') {
j = ht[j].lchild;
} else if(code[i] == '1') {
j = ht[j].rchild;
} else {
return 0;
}
if(ht[j].lchild == -1 && ht[j].rchild == -1) {
DeCode[i] = ht[j].data;
j = 0;
}
i++;
}
DeCode[i] = '\0';
return 1;
}
int main()
{
int w[] = {6, 3, 4, 8, 5, 5};
char c[] = {'a', 'b', 'c', 'd', 'e', 'f'};
int nums = 6;
HTNode HT[2*nums-1];
HCode HC[nums];
initHT(HT, nums, w, c);
CreateHT(HT,nums);
printHT(HT, nums,c);
printf("=============================================================\n");
CreateHCode(HT, HC, nums);
printHCode(HT, HC, nums);
char code[]="10010001101111000"; //abdefad
printf("\n\n待译码信号为:");
for(int k=0;code[k]!='\0';k++)
{
printf("%c",code[k]);
}
printf("\n");
char DeCode[20]; //保存译码后的字符串
if( EnHCode(code,HT,DeCode,nums) )
{
printf("\n\n成功译码!");
printf("\n译码后:");
int i=0;
while(DeCode[i] != '\0')
{
printf("%c",DeCode[i]);
i++;
}
}
else
{
printf("\n\n代码错误!");
}
return 0;
}
【实验总结(个人心得)】
深入地了解了Huffman编码的原理和实现方法,实现了构造一棵Huffman树,输出树各节点的值,输出各字符对应的Huffman编码。但是,发现对于输入信号进行译码这部分内容实验失败。这可能是因为Huffman树构造或Huffman编码运算存在问题,导致无法正确地进行译码。