問題 J: 赫夫曼編碼
題目描述
赫夫曼編碼能夠產生最短的報文。以報文“ABCDABCDABCABDABAA”為例,A編為0,B對應10,C對應110,D對應111,整體的報文長度為35位二進位。相比於定長的ASCII碼,壓縮比達到了18*8/35=4.1。
輸入
輸入有一系列的字串組成,每個字串佔據一行。字串僅包含大寫字母和底線。字串“END” 表示處理結束,不應對其處理。
輸出
對每一個字串,輸出其ASCII編碼的位元位長度,赫夫曼編碼的位元位長度,以及二者之比,精確到小數點後一位。
範例輸入ABCDABCDABCABDABAA AAAAAAAAAAAAAAAAAAEND範例輸出144 35 4.1 144 18 8.0
我費了九牛二虎之力,最終做了出來,代碼如下,以作紀念:
#include <stdio.h>#include<string.h>#define N 100#define MAX 1000#define OK 1typedef struct //huffman節點{char data;int weight;//權值int parent;//記錄父結點下標int lchild;//左孩子int rchild;//右孩子}HTNode;int CreateHT(HTNode ht[],int n)//構造huffman樹的代碼沒有一點錯誤{int i,k,lnode,rnode;int 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;//lnode和rnode為最小權重的兩個節點的位置for(k=0;k<=i-1;k++)if(ht[k].parent==-1)//只在尚未構造二叉樹的結點中尋找{if(ht[k].weight<min1)//在節點中找到兩個最小的,min1和min2記錄下標{min2=min1;rnode=lnode;min1=ht[k].weight;lnode=k;//記錄下最小權重的下標}else if(ht[k].weight<min2){min2=ht[k].weight;rnode=k;}}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;}return OK;}int AddNum(HTNode ht[],int hcd[],int k){int i,f;if(k==1)//結點分兩種情況,一種是只有一個結點{hcd[0]=1;return 0;}else//另一種是大於等於兩個結點{ for(i=0;i<k;i++){ hcd[i]=0; f=ht[i].parent; while(f!=-1)//向上回溯尋找父結點,直至找到整棵樹的根節點 { hcd[i]++;//父結點不為空白,那數目就自增1(Huffman編碼長度增1) f=ht[f].parent; } }}return OK;}int main(){ char str1[MAX]; HTNode node[MAX];int hcd[MAX];//用於記錄每個編碼的長度int i,j,k,m;float s;while(1){ k=0;scanf("%s",str1);if(strcmp(str1,"END")==0) continue; for(i=0;str1[i]!='\0';i++){node[i].weight=0; for(j=0;j<k;j++)//從第一位開始搜尋,如果找到了相同的字元,那麼該字元權值增1 if(node[j].data==str1[i]) { node[j].weight=node[j].weight+1; break;//找到了就不必再迴圈,讀下一個字元 } if(j==k)//沒找到,就加入node數組 { node[k].data=str1[i]; node[k].weight=node[k].weight+1; k++; }}//最後得到的k為字元的種類數目m=i;//記錄下i的值,i為輸入的總字元的個數 CreateHT(node,k);//構建一棵Huffman樹AddNum(node,hcd,k);//擷取個個字元HUffman編碼的長度for(i=0,s=0.0;i<k;i++)s=s+hcd[i]*node[i].weight;//擷取用Huffman編碼時總字元長度printf("%d %.0f %.1f\n",m*8,s,m*8/s);}return 0;}