设计一个利用哈夫曼算法的编码和译码系统,重复地显示并处理以下项目,直到选择退出为止。
将权值数据存放在数据文件(文件名为data.txt,位于执行程序的当前目录中)
分别采用动态和静态存储结构
初始化:键盘输入字符集大小n、n个字符和n个权值,建立哈夫曼树;
编码:利用建好的哈夫曼树生成哈夫曼编码;
输出编码;
设字符集及频度如下表:
字符 空格 A B C D E F G H I J K L M
频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20
字符 N O P Q R S T U V W X Y Z
频度 57 63 15 1 48 51 80 23 8 18 1 16 1
译码功能;
显示哈夫曼树;
界面设计的优化。
typedef struct HTNode{
char c;
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
定义一个结构体表示哈夫曼树,结构体中包括输入的字符、该字符的权值,以及结点的双亲、左孩子、右孩子的下标变量。将哈夫曼树的各结点存储在由HuffmanTree定义的动态分配的数组中。
typedef char **HuffmanCode;
由于每个哈夫曼编码是变长编码,使用一个指针数组来存放每个字符编码串的首地址,各字符的哈夫曼编码存储在由HuffmanCode定义的动态分配的数组HC中,数组的0号单元不使用,从1号单元开始使用。
将所有单元中的双亲、左孩子、右孩子的下标都初始化为0,再输入n个单元中叶子结点的字符、权值。通过n-1次的选择、删除与合并来创建哈夫曼树。选择功能另有函数说明,删除即将结点s1 和 s2 的双亲改为其下标值;合并即将s1 和 s2 的权值和作为一个新的权值依次存入到数组的第n+1 之后的单元中,同时记录这个新结点左孩子的下标为s1 ,右孩子的下标为s2。此模块的函数原型如下:
void CreatHuffmanTree(HuffmanTree &HT, int n)
构造哈夫曼树的流程图如图一所示。
从1~n数组中选择双亲为0且权值最小的两个树根结点s1 和 s2。此模块的函数原型如下:
void Select(HuffmanTree HT,int i,int &s1,int &s2)
在构造哈夫曼树之后,依次以叶子为出发点,向上回溯至根结点为止。回溯时走左分支则生成代码0,走右分支则生成代码1。此模块的函数原型如下:
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
从终端输入一串字符串,将字符串中的字符依次与哈夫曼树中的字符进行比较,若相等,则输出相应的哈夫曼编码,若在哈夫曼树中找不到该字符,则提示输入字符有误。此模块的函数原型如下:
void EnCoding(HuffmanTree HT,HuffmanCode HC,int n)
从终端输入一串0/1字符串,从哈夫曼树的根结点出发,若当前读入0,则走向左孩子,否则走向右孩子,直到HT[i]的左右孩子均为0,输出该数组单元的字符。然后重新从根结点出发继续译码,直到遇到字符串结束标志”\0”。 此模块的函数原型如下:
void DeCoding(HuffmanTree HT,int n)
void CreatHuffmanTree(HuffmanTree &HT, int n)
//creat a huffmantree which is HT
{
int s1 , s2;
if(n <= 1) return ;
int m = 2 * n - 1;
HT = new HTNode[m+1];
for(int i = 1;i <= m;++i) {
HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;
}
cout << "Enter the char and weight"< for(int i = 1;i <= n;i++){ cin >>HT[i].c>> HT[i].weight; } for(int 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; } } 函数参数为指向哈夫曼树的指针的引用以及字符个数n,在此函数中初始化并创建哈夫曼树。 void Select(HuffmanTree HT,int i,int &s1,int &s2) //Select sub-function { int j,k=1; //s1 is the least of HT[].weight while(HT[k].parent!=0) //s2 is the second least of HT[].weight k++; s1=k; for(j=1;j<=i;++j) if(HT[j].parent==0&&HT[j].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 s2=j; } 在HT[k](1≤k≤i-1)中选择两个其双亲域为0且权值最小的结点,即依次与数组中的借点信息进行比较,找到最小权值的数组下标并返回其值。 void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n) // Create the HuffmanCode { int start,c,f; HC = new char*[n+1]; char *cd = new char[n]; cd[n-1] = '\0'; cout< for (int i = 1;i <= n;i++){ start = n-1; c = i; f = HT[i].parent; while (f != 0){ --start; if(HT[f].lchild == c) cd[start] = '0'; else cd[start] = '1'; c = f; f = HT[f].parent; } HC[i] = new char[n - start]; strcpy(HC[i],&cd[start]); cout << HT[i].c; cout << " 's Huffman code is: "< } delete cd; } 在构造哈夫曼树之后,依次以叶子为出发点,向上回溯至根结点为止。回溯时走左分支则生成代码0,走右分支则生成代码1。将生成的代码先从后往前依次存放在一个临时的一维数组cd中,并设一个变量start记录编码在cd中的起始位置(start初始时指示cd的结束位置)。当某字符编码完成时,从cd的start处将编码复制到该字符相应的编码串中。 void EnCoding(HuffmanTree HT,HuffmanCode HC,int n) { int len,i,j; char str[1000]; cout << "Enter a string and decode it " << endl; cin >> str; len = strlen(str); cout << "The relevant 0/1 numbers is :" << endl; for(i = 0;i for(j=1;j<=n;j++){ if(str[i] == HT[j].c) cout << HC[j]; } } cout << endl; }从终端输入一串字符串,将字符串中的字符HT[m]依次与哈夫曼树中的字符HC[i]进行比较,若相等,则输出相应的哈夫曼编码,若在哈夫曼树中找不到该字符,则提示输入字符有误。 void DeCoding(HuffmanTree HT,int n) // Enter a 0/1 string and decode it { int len,i,p; int k1,k2; char str[1000]; cout<<"Enter a seious of 01 code "< cin>>str; len = strlen(str); cout << "The relevant chars is :" << endl; for(i = 0;i p = 2*n-1; while(HT[p].lchild != 0 && HT[p].rchild != 0){ if(str[i] == '0') p = HT[p].lchild; if(str[i] == '1') p = HT[p].rchild; i++; } cout << HT[p].c; } cout << endl; } 从终端输入一串0/1字符串,从哈夫曼树的根结点出发,若当前读入0,则走向左孩子,否则走向右孩子,直到HT[i]的左右孩子均为0,输出该数组单元的字符。然后重新从根结点出发继续译码,直到遇到字符串结束标志”\0”。3.2选择权值最小的结点
3.3生成哈夫曼编码
3.4编码
3.5译码