二.实验内容:
题目:哈夫曼编码/译码
问题描述:
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发编写一个哈夫曼码的编/译码系统。
基本要求:
(2)编码:利用已建好的哈夫曼树,对电文进行编码。
(3)打印编码规则:即字符与编码的一一对应关系。
(4)打印显示电文以及该电文对应的哈夫曼编码。
(5)接收原始数据(哈夫曼编码):从终端输入一串哈二进制哈夫曼编码(由
0和1构成)。
(6)译码:利用已建好的哈夫曼树对该二进制编码进行译码。
(7)打印译码内容:将译码结果显示在终端上。
三. 实验方案
(一)算法设计思路:
1.实现哈夫曼编码首先需要构建最优二叉树,权值越大的叶节点越靠近根节点,其算法为:键盘输入的字符串长度决定最优二叉树的节点数,遍历这个字符串长度,创建具有字符长度n的单节点树。选取根节点权值最小和次小的两个根节点合成一棵树,重复这个过程——把根节点最小和次小的结合直到每个节点都出现在最优二叉树上。
2.构造哈夫曼编码:
左分支为0,右分支为1,各结点所对应的二进制编码为该节点的哈夫曼编码。采用叶节点向上回溯的方法,每退回一个就记录一位数字。将所得编码存入code[]。
3.编码:
根据所得哈夫曼树对比字符串,根据左分支为0右分支为1输出其对应编码。
4.解码:根据哈夫曼树回溯编码。
(二)使用模块及变量的说明(例如结构体的定义含义)
Huffmancode为根据哈夫曼编码求出编码函数。
Maxval为最大值。
typedef struct
{
int weight;
int lchild, rchild;
int parent;
}HTNode, * HuffmanTree;树的结点结构体
void Select(HuffmanTree& ht, int i, int& s1, int& s2)构建最优二叉树
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n);//根据哈夫曼树求出哈夫曼编码
void decode(hufmtree tree[]);//依次读入电文,根据哈夫曼树译码
Huffman()为建立哈夫曼树函数。
四. 实验步骤或程序(经调试后正确的源程序)
#pragma warning(disable:4996)
#include
#include
#include
using namespace std;
typedef struct
{
int weight;
int lchild, rchild;
int parent;
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
void Select(HuffmanTree& ht, int i, int& s1, int& s2)
{
int j, k;
k = s1;
for (j = 1; j < i + 1; j++)
if (s1 != j && j != s2 && ht[j].parent == 0)
{
s1 = j; break;
}
for (j = 1; j < i + 1; j++)
if (s2 != j && s1 != j && j != k && ht[j].parent == 0)
{
s2 = j; break;
}
for (j = 1; j <= i; j++)
if (ht[j].weight < ht[s1].weight && ht[j].parent == 0)s1 = j;
if (s1 == s2)
{
for (j = 1; j < i + 1; j++)
if (s2 != j && s1 != j && j != k && ht[j].parent == 0)
{
s2 = j; break;
}
}
for (j = 1; j <= i; j++)
if (ht[j].weight < ht[s2].weight && ht[j].parent == 0 && j != s1)
s2 = j;
}
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n)
{
int i, s1, m, s2, start;
char* cd;
int c, f;
if (n <= 1) return;
m = 2 * n - 1;
HT = new HTNode[m + 1];
for (i = 1; i <= n; i++)
{
HT[i].weight = w[i - 1];
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (i = n + 1; i <= m; i++)
{
HT[i].weight = 0;
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
cout << "\n哈夫曼树的构造过程如下所示:" << endl;
for (i = n + 1; i <= m; i++)
{
Select(HT, i - 1, s1, s2);
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 = new char* [n + 1];
cd = new char[n];
cd[n - 1] = '\0';
for (i = 1; i <= n; ++i)
{
start = n - 1;
for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)
if (HT[f].lchild == c) cd[--start] = '0';
else cd[--start] = '1';
HC[i] = new char[n - start];
strcpy(HC[i], &cd[start]);
}
delete cd;
}
int main()
{
HuffmanTree HT;
HuffmanCode HC;
char s[50], s2[20]; int i = 0, count = 0; int w[50] = { 0 };
cout << "请输入需要编码的字符串:" << endl;
cin >> s;
while (s[i] != '\0')
{
int j;
for (j = 0; j < count; j++)
if (s[i] == s2[j])
{
w[j]++; i++; break;
}
if (j == count)
{
s2[count] = s[i];
w[count]++;
i++; count++;
}
}
HuffmanCoding(HT, HC, w, count);
cout << "字符\t\t 频数\t\t编码" << endl;
for (i = 0; i < count; i++)
cout << s2[i] << "\t\t" << w[i] << "\t\t" << HC[i + 1] << endl;
string s1, s3;
cout << "输入要编码的英文:" << endl;
cin >> s1;
cout << "编码:" << endl;
for (i = 0; i < s1.length(); i++)
{
for (int j = 0; j < count; j++)
{
if (s1[i] == s2[j])
{
cout << HC[j + 1];
break;
}
}
}
cout << endl;
cout << "输入要解码的哈弗曼码:" << endl;
cin >> s3;
char h[10];
int k = 0, l, j;
cout << "解码:" << endl;
while (k <= s3.length())
{
for (int i = 1; i <= count; i++)
{
l = k;
for (j = 0; j < strlen(HC[i]); j++, l++)
{
h[j] = s3[l];
}
h[j] = '\0';
if (strcmp(HC[i], h) == 0)
{
cout << s2[i - 1];
k = k + strlen(HC[i]);
break;
}
}
}
cout << endl;
return 0;
}
五.程序运行结果(程序运行结果的一些截图)
六.实验总结(调试过程中遇到哪些问题,怎么解决的)
这次的实验是对树的综合运用,其过程基本为根据键盘输入的字符串构造最优二叉树,构造字符串对应的哈夫曼编码,实现解码与编码的操作。这次的操作是对于数据转为树的操作,涉及到树的构建和处理。其中遇到了许多算法方式得问题和对于软件实验不熟悉的问题。例如遇到的c4996问题是没有遇过的问题,经过查询得知在头文件加入#pragma warning(disable:4996)可无视这个错误。这次的实验我做了很久,主要是对于树的知识掌握不够详细,许多问题需要暂停下来查阅才能做到,希望在之后的学习中更加用心,争取做到更好。