一. 设计思路
1. 生成明文的 { 字符 + 权 } 集
2. 构造Huffman树(贪心法 + 自底向上)
3. 利用Huffman树,对明文进行编码
4. 利用huffman树,对编码译码为明文
二. 生成明文的 { 字符 + 权 } 集
1. 字符表:包括字符项目和权值项。
<span style="font-size:14px;">#define N 256 //字符表 struct charcode { char c; //字符 int w; //权值 }; struct charcode ctable[N]; //全局变量,明文中出现的字符个数 int num = 0;</span>
<span style="font-size:14px;">//在字符表中查找字符,成功找到则返回字符位置;不成功返回-1 int find (char ch) { int i; for(i = 0; ctable[i].c != 0; i++) { if(ctable[i].c == ch) //找到字符 { return i; } } return i;//未找到 }</span>
<span style="font-size:14px;">//在字符表中查找字符,成功返回字符位置;不成功返回0 //然后记录字符出现的频率(权值) void getweight() { char ch; freopen("plaintext.txt", "r", stdin); while((ch=getchar())!=EOF) { int i = find(ch);//查找字符位置 if(!ctable[i].c)//若字符表相应的位置未存放字符 { ctable[i].c = ch;//存入字符 num++; //字符数加1 } ctable[i].w++; //权值加1 } fclose(stdin); }</span>
1. Huffman树结构:字符,权,父亲下标,左孩子下标,右孩子下标
<span style="font-size:14px;">//Huffman树结构:字符,权,父亲下标,左孩子下标,右孩子下标 struct treenode//静态链表 { char c; //char int w; //weight int f; //father int l; //left child index int r; //right child index }; //N个外部结点,总结点不超过2N - 1 struct treenode htree[2 * N - 1];</span>
<span style="font-size:14px;">//使用选择法进行排序 void sort() { int i,j; struct charcode t; for(i = 0; i < num; i++) //每次选择最小的权值 { int m = i; //m记录最小权值的下标 for(j = i + 1; j < num; j++) { if(ctable[j].w < ctable[m].w) { m = j; } } t = ctable[m]; ctable[m] = ctable[i]; ctable[i] = t; } }</span>
<span style="font-size:14px;">//使用贪心法建立Huffman树,每次选择权值最小的根节点 void huffman() { int i, j, k, n; for(i = 0; i < num; i++) { //初始化哈夫曼表 htree[i].c = ctable[i].c; htree[i].w = ctable[i].w; htree[i].l = htree[i].f = htree[i].r = -1; //printf("[%d]%c: %d\n",i, htree[i].c, htree[i].w); } j = 0;//j记录叶子节点的下标 k = num;//K记录内部节点的下标 for(n = num; n < 2 * num -1; n++) {//每次选择权值最小 int r = 0, s = 0; htree[n].l = htree[n].f = htree[n].r = -1; while(r < 2) { if(htree[k].w == 0 || htree[k].w > htree[j].w && j < num) { s+=htree[j].w;//加入父亲的权值 if(r == 0) { htree[n].l = j;//左孩子 } else { htree[n].r = j;//右孩子 } htree[j].f = n;//父亲 j++; } else {//选择内部结点 s+=htree[k].w; if(r == 0) { htree[n].l = k; } else { htree[n].r = k; } htree[k].f = n; k++; } r++; } htree[n].w = s;//父亲的权值 } }</span>
<span style="font-size:14px;">//从plaintext.txt中读入明文,生成编码,存入telgraph.txt中 void encode() { char ch; freopen("plaintext.txt", "r", stdin); freopen("telegraph.txt", "w", stdout); char* str= (char*)malloc(sizeof(char) * N); while((ch=getchar()) != EOF) { int i = find(ch); memset(str,0,sizeof(char) * N); getcode(i, str); printf("%s", str); } fclose(stdout); fclose(stdin); //free(str); }</span>
<span style="font-size:14px;">//根据字符所在的下标,从叶子节点往上搜索到根节点 //然后逆置得到该字符的huffman编码 void getcode(int i, char* str) { int n, j, l = 0; for(n = i;htree[n].f != -1; n = htree[n].f) {//沿着父亲往上搜索 int m = htree[n].f; if(n == htree[m].l) { str[l++] = '0';//左孩子记为0 } else { str[l++] = '1';//右孩子记为1 } } for(j = 0; j <= (l -1)/2;j++) {//将编码逆置 char t; t = str[j]; str[j] = str[l - 1 -j]; str[l - 1 - j] = t; } str[l] = '\0';//str存放huffman编码,字符串结束标记 }</span>
<span style="font-size:14px;">//从telgraph.txt中读入编码,译码为明文 //存入tanslation.txt void decode() { char ch; freopen("telegraph.txt", "r", stdin); freopen("tanslation.txt", "w", stdout); ch = getchar(); //根据编码,从根节点往下搜索到叶子节点,得到该编码的字符 while(ch != EOF) { int i; for(i = 2 * num - 2; htree[i].l != -1;) { if(ch == '0') { i = htree[i].l; } else { i = htree[i].r; } ch = getchar(); } printf("%c", htree[i].c); } fclose(stdout); fclose(stdin); }</span>
<span style="font-size:14px;">//思路:主函数实现实验任务的基本流程 int _tmain(int argc, _TCHAR* argv[]) { getweight();//记录字符出现的频率(权值) sort();//权值排序 huffman();//建立Huffman树 encode();//编码 decode();//译码 return 0; }</span>