任意ASCII碼格式信息的huffman tree壓縮(編碼)和解壓(譯碼)
作者:牛老師,華清遠見嵌入式學院講師。
計算機實踐中發現,大多數信息的表達都存在着一定的冗餘度,有效的降低這種冗餘度可以使我們用更小的空間存儲更大的數據量,同時在有限的通信帶寬的情況下,可以傳輸更多的信息,等等……。那用什麼辦法可以降低這種冗餘度,這裏我們學習其中的一種huffman treee壓縮(編碼)和解壓(譯碼)方法。
huffman tree的壓縮和解壓的過程如下:
1、描述
對於任意的ASCII格式的信息aba$a@ba*a$a,先統計它們的出現頻率a6,b2,$2,*1,@1,然後分別用不等長的01串來編碼它們,如a1,b00,$011,*0100,@0101,最後用01串替換原來的信息,得到100101110101001010010111,並按二進制位組合,這樣就可以得到壓縮後的文件。
相對應,解壓時必須知道編碼a1,b00,$011,*0100,@0101,然後根據100101110101001010010111一步步解壓得到aba$a@ba*a$a原始信息。
2、分析
爲了解壓成功,首先必須保證任何已經編碼的單個字符不能是其他字符編碼的前綴,也就是說用1編碼a以後,其他字符編碼不能以1開頭。爲了實現壓縮的最小,同時要保證出現頻率最高的字符編碼最短,頻率最低的編碼最長。
3、方法
通過描述和分析,我們明白這種方法是可以實現壓縮文件的,用什麼辦法可以實現這種方法呢?使用最優二叉樹也就是huffman tree可以實現上面的方法,步驟如下:
1) 將a6,b2,$2,*1,@1按照頻率值從小到大有序入隊,出隊兩個元素,分別以它們爲左右子樹,建立二叉樹,並將它們出現次數之和作爲根節點,把根節點有序入隊。
@1 *1 $2 b2 a6 |
2 $2 b2 a6 / \ @1 *1 |
2) 然後重複1步驟直到隊中只剩下一個節點,該節點就是所求huffman tree的根節點,最後按照左0,右1進行編碼a1,b00,$011,*0100,@0101。
b2 4 a6 / \ 2 $2 / \ @1 *1 |
6 a6 / \ b2 4 / \ 2 $2 / \ @1 *1 |
12 0/ 1\ 6 a6 0/ 1\ 1 b2 4 00 0/ 1\ 2 $2 0/ 1\ 011 *1 @1 0100 0101 |
4、步驟
第一步:統計aba$a@ba*a$a字符出現的頻率,a6,b2,$2,*1,@1。
第二步:去除重複的字符ab$@*。
第三步:創建huffman tree,對ab$@*進行編碼a1,b00,$011,*0100,@0101。
第四步:aba$a@ba*a$a根據編碼a1,b00,$011,*0100,@0101壓縮爲10010111010
1001010010111。
第五步:100101110101001010010111根據huffman tree解壓爲aba$a@ba*a$a。
5、實現
treehuffman.c
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <string.h>
4. #include "treehuffman.h"
5. #include "../queue/queuelink.h"
6.
7. treehuffman *treecreate(char *s)
8. {
9. int w[256] = {};
10. int i;
11. char c[256] = {}, *pc = c;
12. queuelink *q = queuecreate();
13. queuedata temp;
14.
15. // 第一步
16. while(*s != '\0')
17. {
18. w[(int)*s++]++;
19. }
20.
21. // 第二步
22. for(i=0; i<256; i++)
23. {
24. if(w[i] != 0)
25. {
26. *pc++ = i;
27. }
28. }
29.
30. // 第三步
31. pc = c;
32. while(*pc != '\0')
33. {
34. temp = (treehuffman *)malloc(sizeof(treehuffman));
35.
36. temp->c = *pc;
37. temp->w = w[(int)*pc++];
38. temp->code[0] = '\0';
39. temp->lchild = temp->rchild = NULL;
40.
41. queueenterorder(q, temp);
42. }
43.
44. while(q->front->next != q->rear)
45. {
46. temp = (treehuffman *)malloc(sizeof(treehuffman));
47.
48. temp->code[0] = '\0';
49. temp->lchild = queuedelete(q);
50. temp->rchild = queuedelete(q);
51. temp->w = temp->lchild->w+temp->rchild->w;
52.
53. queueenterorder(q, temp);
54. }
55.
56. return queuedelete(q);
57. }
58.
59. // 第四步
60. char *treecompress(treehuffman *t, char *s, char *compressed)
61. {
62. queuelink *q = queuecreate();
63. queuedata temp;
64. char *code[256];
65.
66. queueenter(q, t);
67.
68. while(queueempty(q) == 0)
69. {
70. temp = queuedelete(q);
71.
72. if(temp->lchild==NULL && temp->rchild==NULL)
73. {
74. code[(int)temp->c] = temp->code;
75. printf("%c(%d) : %s\n", temp->c, temp->w, temp->code);
76. }
77.
78. if(temp->lchild != NULL)
79. {
80. strcpy(temp->lchild->code, temp->code);
81. strcat(temp->lchild->code, "0");
82. queueenter(q, temp->lchild);
83. }
84.
85. if(temp->rchild != NULL)
86. {
87. strcpy(temp->rchild->code, temp->code);
88. strcat(temp->rchild->code, "1");
89. queueenter(q, temp->rchild);
90. }
91. }
92.
93. while(*s != '\0')
94. {
95. strcat(compressed, code[(int)*s++]);
96. }
97.
98. return compressed;
99. }
100.
101. // 第五步
102. char *treeuncompress(treehuffman *t, char *compressed, char *compress)
103. {
104. char *p = compress;
105. treehuffman *temp = t;
106.
107. while(*compressed != '\0')
108. {
109. if(*compressed++ == '0')
110. {
111. temp = temp->lchild;
112. }
113. else
114. {
115. temp = temp->rchild;
116. }
117.
118. if(temp->lchild==NULL && temp->rchild==NULL)
119. {
120. *compress++ = temp->c;
121. temp = t;
122. }
123. }
124.
125. return p;
126. }
1treehuffman.h
127. #ifndef __TREEHUFFMAN_H__
128. #define __TREEHUFFMAN_H__
129.
130. typedef char treedata;
131.
132. typedef struct tree
133. {
134. treedata c;
135. int w;
136. char code[16];
137. struct tree *lchild, *rchild;
138. }treehuffman;
139.
140. extern treehuffman *treecreate(char *s);
141. extern char *treecompress(treehuffman *t, char *s, char *compressed);
142. extern char *treeuncompress(treehuffman *t, char *compressed, char *compress);
143.
144. #endif
注意:對於a6,b2,$2,*1,@1來說,結果可能有幾種,可能是a1,b00,$011,*0100,@0101,也可以是a1,$00,b011,@0100,*0101。這是因爲統計的頻率中有相等的,所以在有序入隊時他們可能是左子樹,也可能是右子樹,所以出來的結果可能與程序本身的處理有關,不過不影響最終的壓縮率。