- 相關(guān)推薦
哈夫曼編碼程序設(shè)計
算法與數(shù)據(jù)結(jié)構(gòu)課程設(shè)計
哈夫曼編碼/譯碼器設(shè)計
學(xué)生姓名: 學(xué) 號:
專 業(yè):(計算機(jī)科學(xué)與技術(shù)) 年 級:(大二) 指導(dǎo)教師:(汪洋)
2009年6月19日
哈夫曼編碼/譯碼器
問題描述:
利用哈夫曼編碼進(jìn)行通信可以大大提高信道利用率,縮短信息傳輸時間,降低傳輸成本,但是,這要求在發(fā)送端通過一個編碼系統(tǒng)對待傳數(shù)據(jù)預(yù)先編碼,在接收端將傳來的數(shù)據(jù)進(jìn)行譯碼(復(fù)原)。對于雙工信道(即可以雙向傳輸信息的信道)每端都需要一個完整的編/譯碼系統(tǒng)。試為這樣/譯碼系統(tǒng)。
基本要求:
I:初始化(Initialization)。從終端讀入字符集大小n,以及n個字符和n個權(quán)值,建立哈夫曼樹,并將它存于文件hfmTree中。
E:編碼(Encoding)。利用已建好的哈夫曼樹(如不在內(nèi)存,則從文件hfmTree中讀入),對文件ToBeTran中的正文進(jìn)行編碼,然后將結(jié)果存入文件CodeFile中。
D:譯碼(Decoding)。利用已建好的哈夫曼樹將文件CodeFile中的代碼進(jìn)行譯碼,結(jié)果存入文件TextFile中。
P:打印代碼文件(Print)。將文件CodeFile以緊湊格式顯示在終端上,每行50個代碼。同時將此字符形式的編碼文件寫入文件CodePrin中。
T:打印哈夫曼樹(Tree printing)。將已在 中的哈夫曼樹以直觀的方式(樹或凹入表形式)顯示在終端上,同時將此字符形式的哈夫曼樹寫入文件TreePrint中。
大體解題思路:
(1)對輸入的一段欲編碼的字符串進(jìn)行統(tǒng)計各個字符出現(xiàn)的次數(shù),并它們轉(zhuǎn)化為權(quán)值{w1,w2,……,wN}構(gòu)成n棵二叉樹的集合F={T1,T2,……,Tn}把它們保存到結(jié)構(gòu)體數(shù)組HT[n]中,其中{Ti是按它們的ASCⅡ碼值先后排序。其中每棵二叉樹Ti中只有一個帶權(quán)為Wi的根結(jié)點的權(quán)值為其左、右子樹上根結(jié)點的權(quán)值之和。
(2)在HT[1..i]中選取兩棵根結(jié)點的權(quán)值最小且沒有被選過的樹作為左右子樹構(gòu)造一棵新的二叉樹,且置新的二叉樹的根結(jié)點的權(quán)值為左、右子樹上根結(jié)點的權(quán)值之和。
(3)哈夫曼樹已經(jīng)建立后,從葉子到根逆向求每一個字符的哈夫曼編碼。
概要設(shè)計:
實現(xiàn)的功能:1.查看原文(showpassage()),2.字符統(tǒng)計(showdetail()),
3.哈夫曼編碼并輸出內(nèi)容(HuffmanCoding(HT,HC,30,w)),4.輸出整篇文章的碼字(printcode()),5.求最小權(quán)值(minweight()),6.最碼字進(jìn)行解碼(decode()),7.測試解碼(testdecode()),8.推出(break)。
(1) 定義結(jié)構(gòu)體HTNOde,*HuffmanTree;typedefchar**HuffmanCode;
定義堆結(jié)構(gòu)體RedType;定義全局變量;
(2) 編程序:測試編碼(void testdecode());解碼(void decode);求最
小權(quán)值(void minweight());打印碼字(void printcode());堆排序(void HeapAdjust(SqList&L,int s,int
m))
;
哈
夫
曼
編
碼
(void
HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,float *w,int n ));輸出個字符的次數(shù)比例(void showdetail());輸出原英文文章并做統(tǒng)計(void showpassage());
(3)主函數(shù)
詳細(xì)設(shè)計: (1) 定義結(jié)構(gòu)體
1.哈夫曼結(jié)構(gòu)體 typedef struct {
float weight;
unsigned int parent, lchild, rchild; }HTNode, *HuffmanTree; typedef char **HuffmanCode ; 2.堆結(jié)構(gòu)體 typedef struct {
float key; //關(guān)鍵字項
int otherinfo; //其他數(shù)據(jù)項(此題目中用不到)
}RedType;
typedef struct {
RedType r[MAXSIZE+1]; //r[0]閑置用作哨兵 int length; //順序表長度 }SqList;
3.定義全局變量
HuffmanTree HT; //赫夫曼樹 HuffmanCode HC; //碼值
FILE *fp, *fp1, *fp2; int a[30] = {0}; float b[30];
float *w; //權(quán) (2)程序代碼
1.測試解碼(可以輸入一個不正確的二進(jìn)制碼串) void testdecode() {
char str[200]; //存放自己輸入的碼子 int p, p1, i; //解碼的線索 char ch; 內(nèi)):\n");
gets(str);
printf("以上碼子解碼后為:\n");
p = 59; //最初令p為樹根整數(shù),p由根順著樹枝一直到樹葉
i = 0; ch = str[i++]; while (ch!='\0') {
printf("\n請根據(jù)以上各個字符的編碼輸入一串二進(jìn)制碼字(200個以
查 !\n");
{
if (ch == '0') {
p = HT[p].lchild; } {
p = HT[p].rchild; } else {
printf("\n你輸入了'0','1'之外的字符,無法正確譯碼,請檢 return ; //直接結(jié)束 }
ch = str[i++]; //下一個碼字 不斷的取下一個
else if(ch == '1')
}
if(p
switch (p) {
case 27 : printf(",");
break;
case 28 : printf("."); break;
case 29 : printf(" "); break;
case 30 : printf("\n"); break;
}
p1 = p; //讓p1記住p p = 59; //這里p要重置為59,因為經(jīng)過上面的程序p已經(jīng)變化了,不重置為1則HT[p].lchild!=0||HT[p].rchild!=0所以for語句無法進(jìn)行!!!!
} //while循環(huán) if (p1 > 30)
printf("\n以上正確譯出了前面的字符,由于你輸入的二進(jìn)制碼不完整,最后一位字符無法譯出,請檢查!\n");
}
2.下面是解碼 void decode() { int p; char ch;
printf("\n\n對碼子解碼后的如下:\n"); fp1 = fopen("bianma.txt","r"); if(!fp1) { printf("can not open this file!\n"); exit(0);
}
p = 59; 個非零整數(shù),p由根順著樹枝一直到樹葉
ch = fgetc(fp1); fp2 = fopen("jiema.txt","w"); if (!fp2) {
printf("can not open this file!\n");
//最初令p為任意一
exit(0);
}
while (ch!=EOF) { for ( ; HT[p].lchild!=0||HT[p].rchild!=0 ; ) 下一個
{ if (ch == '0') {
p = HT[p].lchild; }
else {
p = HT[p].rchild;
}
ch = fgetc(fp1); } switch (p) {
case 27 : printf(","); fprintf(fp2,","); break;
case 28 : printf("."); fprintf(fp2, "."); break;
case 29 : printf(" ");
fprintf(fp2, " "); break;
case 30 : printf("\n");
fprintf(fp2, "\n");
break;
//下一個碼字 不斷的取
default : printf("%c", p+96);
fprintf(fp2, "%c", p+96);
} p = 59; //這里p要重置為59,因為經(jīng)過上面的程序p已經(jīng)變化了,不重置為1則HT[p].lchild!=0||HT[p].rchild!=0所以for語句無法進(jìn)行!!!!
} //while循環(huán) printf("\n"); fprintf(fp2, "\n"); fclose(fp1); fclose(fp2); }
3.求最小權(quán)值 void minweight() {
float Weight = 0; int i;
for (i = 0 ; i
Weight = Weight + strlen(HC[i+1])*b[i]; printf("最小權(quán)值是:%f\n",Weight); }
4.打印碼子 void printcode() { char ch;
fp = fopen("Huffman.txt","r"); if (!fp) {
printf("can not open this file!\n"); }
fp1 = fopen("bianma.txt","w");
exit(0);
if (!fp1) { printf("can not open this file!\n"); exit(0);
}
printf("\n原英文文章經(jīng)編碼后如下:\n"); ch = fgetc(fp); while (ch!=EOF) {
if (ch > 96 && ch
printf("%s", HC[ch-96]);
fputs(HC[ch-96], fp1); ch = fgetc(fp); continue; }
if (ch > 64 && ch
fputs(HC[ch-64], fp1); ch = fgetc(fp); continue; } if (ch == ',')
{ printf("%s", HC[27]); fputs(HC[27], fp1); ch = fgetc(fp);
continue; } if (ch == '.')
{
//小寫字母 //輸出到文件里面 //大小字母 //輸出到文件里面
printf("%s", HC[28]); f第一文庫網(wǎng)puts(HC[28], fp1); ch = fgetc(fp); continue;
} {
if (ch == ' ')
printf("%s", HC[29]); fputs(HC[29], fp1); ch = fgetc(fp); continue;
} {
if (ch == '\n')
printf("%s", HC[30]); fputs(HC[30], fp1); ch = fgetc(fp); continue;
}
}
printf("\n\n"); fclose(fp); fclose(fp1); } 5.堆排序
void HeapAdjust(SqList &L, int s, int m) {
RedType rc; int j; rc = L.r[s];
for (j = 2 * s ; j
{
if (j L.r[j+1].key) //即使等于也不要動,不用加1
++j; break; if (rc.key
}
L.r[s] = rc;
}
void select(HuffmanTree t, int i, int &s1, int &s2)
{ //此函數(shù)被調(diào)用一次則就用堆排序選擇兩個最小的賦給s1和s2
SqList L;
RedType rc;
int j, k, n = 1;
L.length = 0;
for (j = 1 ; j
{ if (t[j].parent!=0)
continue;
L.r[n].key = t[j].weight; //賦值好,用堆排序
}
for (k = L.length/2 ; k > 0 ; --k)
HeapAdjust(L,k,L.length);
s1 = L.r[1].otherinfo; //最小的選出來了!
/***把最小的換到最下面***/
rc = L.r[1];
L.r[n].otherinfo = j; //存放序號,j是一直在加一的,n++; L.length++; //這樣寫很巧妙的 循環(huán)一次加1,但是n不是的只有在符合條件的情況下才加1
L.r[1] = L.r[L.length]; //此次的select函數(shù),進(jìn)行堆排序的只有L.length 個(parent!=0的沒有在里面),所以這里是L.length而不是i
L.r[L.length] = rc;
HeapAdjust(L,1,L.length-1);
s2 = L.r[1].otherinfo; //次小的選出來了(這里當(dāng)輸入1 4 2 8四個數(shù)時,第一次選出的s1=1,s2=3是對的,但第二次選出的s1=5,但s2是隨機(jī)數(shù))
}
6.赫夫曼編碼
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, float *w, int n) // 算法6.12
{ // w存放n個字符的權(quán)值(均>0),構(gòu)造赫夫曼樹HT,并求出n個字符的赫夫曼編碼HC
int m, i, s1, s2, start, k;
unsigned c, f;
HuffmanTree p;
char *cd;
if (n
return;
m = 2 * n - 1;
w = (float *)malloc(30*sizeof(float));
for (i = 0 ; i
*(w+i) = b[i];
HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode)); // 0號單元未用 for (p = HT + 1, i = 1 ; i
{
(*p).weight = *w;
(*p).parent = 0;
(*p).lchild = 0;
(*p).rchild = 0;
for (i = n + 1 ; i
(*p).parent=0;
for (i = n + 1 ; i
{ // 在HT[1~i-1]中選擇parent為0且weight最小的兩個結(jié)點,其序號分別為s1和s2
select(HT, i-1, s1, s2);
HT[s1].parent = i;
HT[s2].parent = i; //最小的和次小的雙親已經(jīng)不為0了,下次就不在它兩中間找最小的和次小的了。
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
} //建立赫夫曼樹也容易,關(guān)鍵是select這個子函數(shù)!!! printf("赫夫曼樹如下表:\n") ;
printf("__________________________________________________________________\n")
\n");
for(k = 1 ; k
{
if (k
printf("|___%2d____|__%c
和%c__|__%f__|____%2d____|____%2d____|____%2d____|\n",k,k+64,k+96,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if (k == 27)
printf("|___%2d____|___,____|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if (k == 28)
; printf("|_number__|__name__|___weight___|__parent__|__lchild__|__rchild__|
printf("|___%2d____|___.____|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if (k == 29)
printf("|___%2d____|__
[k].parent,HT[k].lchild,HT[k].rchild);
if (k == 30)
printf("|___%2d____|__
[k].parent,HT[k].lchild,HT[k].rchild);
if (k > 30)
printf("|___%2d____|________|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
}
printf("\n") ;
HC = (HuffmanCode)malloc((n+1)*sizeof(char*));
cd = (char*)malloc(n*sizeof(char));
cd[n-1] = '\0';
for (i = 1 ; i
{
start = n-1; //這里f=HT[f].parent是很巧妙的!!! for (c = i, f = HT[i].parent ; f!=0 ; c = f, f = HT[f].parent)//f=HT[i].parent f!=0是結(jié)束條件,所有的編碼最后都要回到HT[m],而只有HT[m]的parent是0!!!
// 從葉子到根逆向求編碼 if (HT[f].lchild==c) //c是左孩子則碼值是0 cd[--start] = '0'; //這樣逆著輸,當(dāng)我們正序輸出的時候就恰好是想要的編碼了!!!
else
空格__|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT換行__|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT
cd[--start] = '1';
HC[i] = (char*)malloc((n-start)*sizeof(char));
// 為第i個字符編碼分配空間 strcpy(HC[i],&cd[start]); //從cd復(fù)制編碼(串)到HC,這里的&cd[start]是一個地址
}
free(cd); // 釋放工作空間 printf("經(jīng)赫夫曼編碼后碼值如下:\n");
for (i = 1 ; i
{
printf("%c和%c---->%f---->", i+64, i+96, HT[i].weight);
puts(HC[i]);
}
printf(" , ---->%f---->%s\n", HT[27].weight, HC[27]);
printf(" . ---->%f---->%s\n", HT[28].weight, HC[28]);
printf("空格---->%f---->%s\n", HT[29].weight, HC[29]);
printf("換行---->%f---->%s\n", HT[30].weight, HC[30]);
}
7.輸出各個字符的次數(shù),比例
void showdetail()
{
int i;
printf("此英文文章中各個字母,個數(shù),比例如下表:\n");
printf("____________________________________________\n"); printf("|__字母__|_____個數(shù)_____|_______比例_______|\n");
for (i = 0 ; i
printf("|__%c和%c__|_____%3d______|_____%f_____|\n", i+97, i+65, a[i], b[i]);
printf("|___,____|_____%3d______|_____%f_____|\n", a[26], b[26]); printf("|___.____|_____%3d______|_____%f_____|\n", a[27], b[27]); printf("|__空格__|_____%3d______|_____%f_____|\n", a[28], b[28]);
printf("|__換行__|_____%3d______|_____%f_____|\n", a[29], b[29]); printf("\n");
}
8.輸出原英文文章,并做統(tǒng)計
void showpassage()
{
float count = 0;
int i;
char ch;
{
printf("can not open Huffman.txt !\n"); exit(0); fp = fopen("Huffman.txt","r"); if (!fp)
}
printf("要編碼的文章如下:\n");
ch = fgetc(fp);
while (ch!=EOF)
{
putchar(ch); //把要編碼的文章輸入到屏幕中 if (ch >= 'a' && ch
{
a[ch-'a']++;
count+=1;
}
if (ch >= 'A' && ch
count+=1;
}
if (ch == ',')
元 a[26]++; //逗號放在27號單元 a[27]++; //句號放在28號單元 a[28]++; //空格符放在29號單元 a[29]++; //換行符放在30號單 if (ch == '.') if (ch == ' ') if (ch == '\n')
ch = fgetc(fp); //a[]的零號單元放a
}
printf("\n\n");
}
(3)主函數(shù)
void main()
{
char c;
char s[200]=" 1.查看原文.\n 2.字符統(tǒng)計.\n 3.赫夫曼編碼并輸出內(nèi)容.\n 4.輸出整篇文章的碼字.\n 5.求最小權(quán)值.\n 6.對碼子進(jìn)行解碼.\n 7.測試解碼.\n 8.退出.\n";
printf("\n\n");
printf("******************************************************\n");
printf("
**\n");
printf(" ** 說明:本程序只對英文文章的52個大小寫字母,逗號,句 **\n");
printf(" ** 號,空格符,換行符進(jìn)行赫夫曼編碼,并且大小寫字母不 **\n");
printf(" ** 區(qū)分,其它字符因為出現(xiàn)的概率太低,for (i = 0 ; i
故本程序沒有考慮 **\n");
printf(" ** ,各個字符出現(xiàn)的頻率對應(yīng)他們的權(quán)值,解碼時可能與原 **\n");
printf(" ** 文有少量的失真,希望用戶理解和支持,謝謝! **\n");
printf("** **\n");
printf("********************************************************\n");
printf("\n\n");
printf("******************************************************\n");
printf(" * 注意:必須按順序先進(jìn)行1,2,3項的操作,否則會有錯誤! *\n");
printf(" * 當(dāng)1,2,3項順次進(jìn)行完后可以任意選擇這8種操作. *\n");
printf("******************************************************\n");
printf("請認(rèn)真閱讀以上注意的內(nèi)容,如果已經(jīng)讀完請按任意鍵開始操作:\n");
c=getch();
system("cls");
do
{
printf("\n請選擇你要的操作:\n\n");
puts(s);
c=getch();
switch (c)
{
case '1' : showpassage(); //這里必須先按 break; //1,2,3先按順序進(jìn)行1,2,3的操作,否則有問題 順序操作完后,則可以任意選擇這8項操作
case '2' : showdetail(); break; break; break; break; break; break; case '3' : HuffmanCoding(HT, HC, w, 30); case '4' : printcode(); case '5' : minweight(); case '6' : decode(); case '7' : testdecode(); case '8' : break; printf("請輸入正確的選項!\n"); } default : putch(7);
}while(c!='8');
printf("\n\nBye Bye ^_^ .!\n\n");
}
調(diào)試分析:
1. 在堆定義中RedType r[MAXSIZE+1],r[0]閑置用作哨兵;
2. 在測試解碼中(p
switch (p)后P重置59是因為因為經(jīng)過上面的程序p已經(jīng)變化了,不重置為1則HT[p].lchild!=0||HT[p].rchild!=0所以for語句無法進(jìn)行!!!!
3. 在解碼void decode()中最初令p為任意一個非零整數(shù),p由根順著樹枝一直到樹葉; switch (p)后p要重置為59,因為經(jīng)過上面的程序p已經(jīng)變化了,不重置為1則HT[p].lchild!=0||HT[p].rchild!=0所以for語句無法進(jìn)行!!!!
4. 堆排序void HeapAdjust(SqList &L, int s, int m) 中 if (j
L.r[j].key > L.r[j+1].key) //即使等于也不要動,不用加1;if (rc.key
5. void select(HuffmanTree t, int i, int &s1, int &s2)
//此函數(shù)被調(diào)用一次則就用堆排序選擇兩個最小的賦給s1和s2
6. void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, float *w, int n) // w存放n個字符的權(quán)值(均>0),構(gòu)造赫夫曼樹HT,并求出n個字符的赫夫曼編碼HC
測試數(shù)據(jù):
權(quán)值的個數(shù)是N=8
測試數(shù)據(jù)為7 19 2 6 32 3 21 10
結(jié)果是:
哈夫曼樹:
5 (2, 3)
11 (5, 6)
17 (7, 10)
28 (11, 17)
40 (19, 21)
60 (28, 32)
100 (40, 60)
7的哈夫曼編碼是 1010
19的哈夫曼編碼是 00
2的哈夫曼編碼是 10000
6的哈夫曼編碼是 1001
32的哈夫曼編碼是 11
3的哈夫曼編碼是 10001
21的哈夫曼編碼是 01
10的哈夫曼編碼是 1011
【哈夫曼編碼程序設(shè)計】相關(guān)文章:
窗簾后邊的考夫曼太太04-30
述評戈夫曼的社會擬劇理論04-27
以人為基礎(chǔ)的正義程序理論--考夫曼法哲學(xué)思想探微05-02
編碼教學(xué)反思04-28
什么是編碼方式04-26
寶天曼05-01
一種新型數(shù)據(jù)編碼方案-簡拼編碼法04-28
一種新型數(shù)據(jù)編碼方案-簡拼編碼法04-29
程序設(shè)計心得11-15