哈夫曼编码/译码系统(树应用)
[问题描述]
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
[实现提示]
在本例中设置发送者和接受者两个功能,
发送者的功能包括:
①输入待传送的字符信息;
②统计字符信息中出现的字符种类数和各字符出现的次数(频率);
②根据字符的种类数和各自出现的次数建立哈夫曼树;
③利用以上哈夫曼树求出各字符的哈夫曼编码;
④将字符信息转换成对应的编码信息进行传送。
接受者的功能包括:
①接收发送者传送来的编码信息;
②利用上述哈夫曼树对编码信息进行翻译,即将编码信息还原成发送前的字符信息。
从以上分析可发现,在本例中的主要算法有三个:
(1)哈夫曼树的建立;
(2)哈夫曼编码的生成;
(3)对编码信息的翻译。
源代码
#include<iomanip> #include<stdlib.h> #include<string.h> #include<stdio.h> #include<iostream> using namespace std; #define maxsize 100 struct frequence//统计频率 { char a;//存放字符 int n;//该字符出现的次数 }; typedef struct { unsigned int weight; // 用来存放各个结点的权值 unsigned int parent, lchild, rchild;//指向双亲、 孩子结点的指针 }HTNode, *HuffmanTree; //动态分配数组存储赫夫曼树 typedef char **HuffmanCode;//动态分配数组存储赫夫曼编码表 int W[maxsize], *w;//存放字符的权值 int c1 = 0;//输入报文的长度 frequence ch[27];//26个英文字母,0号位置不存储字符,用来存储总共的字符个数,多次出现的只记一次 char a[maxsize];//存放所有的字符 int Frequent() { int i = 0; for (; i <= 26; i++)//初始化结构体数组 { ch[i].n = 0; } cout << "输入电报:(输入#键结束输入):"; char c; cin >> c; while (c != '#') { a[c1] = c; bool flag; i = 1; flag = false; for (; i <= ch[0].n; i++) { if (c == ch[i].a) { flag = true; break; } } if (flag)//已存在 { ch[i].n++; } else//不存在 { ch[0].n++; ch[ch[0].n].n++; ch[ch[0].n].a = c; } c1++; cin >> c; } cout << "报文的长度:" << c1 << endl; for (int j = 1; j <= ch[0].n; j++) { cout << ch[j].a << " " << ch[j].n << "\t\t"; W[j - 1] = ch[j].n; } cout << endl; cout << "不重复字符的总个数:" << ch[0].n << endl; w = W; } void Select(HuffmanTree HT, int i, int &s1, int &s2) { int j, k = 1; while (HT[k].parent != 0) k++; s1 = k; for (j = 1; j <= i; ++j) if (HT[j].parent == 0 && HT[j].weight<HT[s1].weight) s1 = j; k = 1; while ((HT[k].parent != 0 || k == s1)) k++; s2 = k; for (j = 1; j <= i; ++j) if (HT[j].parent == 0 && HT[j].weight<HT[s2].weight&&j != s1) s2 = j; } void HuffmanCoding(HuffmanTree &HT, HuffmanCode&HC, int *w, int n) { //w存放n个权值, 构造哈夫曼树p, 并求出哈夫曼编码hc int m, i, s1, s2, start, c, f; HuffmanTree p; if (n <= 1) return; m = 2 * n - 1;//总结点数 HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));//0号单元未用 for (p = HT + 1, i = 1; i <= n; ++i, ++p, ++w) { p->weight = *w; p->parent = 0; p->lchild = 0; p->rchild = 0; } for (; i <= m; ++i, ++p) { p->weight = 0; p->parent = 0; p->lchild = 0; p->rchild = 0; } cout << endl << 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; cout << endl << "HT[" << s1 << "] and HT[" << s2 << "] create"; cout << " HT[" << i << "], weight=" << HT[i].weight << endl; } //从叶子到根逆向求每个字符的赫夫曼编码 HC = (HuffmanCode)malloc((n + 1)*sizeof(char *));//分配n个字符编码的走指针向量 char *cd; cd = (char *)malloc(n*sizeof(char));//分配求编码的工作空间 cd[n - 1] = '\0';//编码结束符 //system("pause"); cout << "哈夫曼编码如下:" << endl; 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] = (char*)malloc((n - start)*sizeof(char));//为第i个字符编码分配空间 strcpy(HC[i], &cd[start]);//从cd复制编码到HC cout << ch[i].a << " 哈夫曼编码是" << HC[i] << endl; } cout << endl; free(cd);//释放空间 } void Decode(HuffmanTree &HT, HuffmanCode&HC, int n)//依次读入电文,根据哈夫曼树译码 { cout << "电报内容:"; for (int k = 0; k <= c1; k++) cout << a[k]; cout << endl; for (int k = 0; k <= c1; k++) for (int y = 1; y <= ch[0].n; y++) if (a[k] == ch[y].a) cout << a[k] << "------哈夫曼编码是" << HC[y] << endl; cout << "电报内容哈夫曼编码:"; for (int k = 0; k <= c1; k++) for (int y = 1; y <= ch[0].n; y++) if (a[k] == ch[y].a) cout << HC[y]; cout << endl; int p, i = 0; char h[50]; p = 2 * n - 1; //从根结点开始往下搜索 cout << "输入原码:"; cin >> h; cout << "\n译码完成:"; while (h[i] != '\0' && p != 0) { if (h[i] == '0') p = HT[p].lchild; //走向左孩子 else p = HT[p].rchild; //走向右孩子 if (!HT[p].lchild && !HT[p].rchild) //tree[i]是叶结点 { cout << ch[p].a; p = 2 * n - 1; //回到根结点 } i++; } cout << endl; if (HT[i].lchild && h[i] != '\0') //电文读完,但尚未到叶子结点 cout << "\n输入原码错误\n"; //输入电文有错 } void menu() { cout << "===================================" << endl; cout << " 哈夫曼编码/译码系统 " << endl; cout << " 1.哈夫曼编码 " << endl; cout << " 2.译码系统 " << endl; cout << " 0.退出 " << endl; cout << "===================================" << endl; } int main() { HuffmanTree HT; HuffmanCode HC; int in; menu(); do{ cout << "请输入操作序号:"; cin >> in; system("cls"); menu(); switch (in){ case 1: Frequent(); HuffmanCoding(HT, HC, w, ch[0].n); system("pause"); break; case 2: Decode(HT, HC, ch[0].n); system("pause"); break; case 0: break; default: cout << "输入错误,请重新输入:"; } } while (in != 0); }