Huffman编码的设计与实现
文章目錄
- (一)設(shè)計描述
- (二)需求分析
- (三)詳細(xì)設(shè)計
- (四)代碼實(shí)現(xiàn)與測試
- (五)個人總結(jié)
(一)設(shè)計描述
1.題目描述
設(shè)計一個利用哈夫曼算法的編碼和譯碼系統(tǒng),重復(fù)地顯示并處理以下項(xiàng)目,直到選擇退出為止。
2.設(shè)計目的與要求
1)目的:
通過布置具有一定難度的實(shí)際程序設(shè)計項(xiàng)目,進(jìn)一步理解和掌握課堂上所學(xué)各種基本抽象數(shù)據(jù)類型的邏輯結(jié)構(gòu)、存儲結(jié)構(gòu)和操作實(shí)現(xiàn)算法
2)要求:
1.要求利用C\C++語言來完成系統(tǒng)的設(shè)計; 2.突出C語言的函數(shù)特征(以多個函數(shù)實(shí)現(xiàn)每一個子功能)或者C++語言面向?qū)ο蟮木幊趟枷?#xff1b; 3.畫出功能模塊圖; 4.進(jìn)行簡單界面設(shè)計,能夠?qū)崿F(xiàn)友好的交互; 5.具有清晰的程序流程圖和數(shù)據(jù)結(jié)構(gòu)的詳細(xì)定義; 6.熟練掌握C語言或者C++語言的各種操作。(二)需求分析
| 第二步 | 定義字符指針P,數(shù)組letter[]和數(shù)組string[],數(shù)組letter用來調(diào)用數(shù)據(jù) input,data,num;string[]用來輸入字符,同時讓指針P指向string[];由于數(shù)組是單個存儲,判斷字符是否相同,用account,num分別統(tǒng)計字母個數(shù)以及每個字符的對應(yīng)次數(shù),即求權(quán)值。 |
| 第三步 | 首先定義兩個數(shù)組huffnode,array[]分別儲存結(jié)點(diǎn)與字符,然后進(jìn)行哈夫曼樹的初始化,然后找到權(quán)值最小的兩個結(jié)點(diǎn),并記錄當(dāng)前下標(biāo)。由于是逆序輸出,左右子樹的結(jié)點(diǎn)位置為2n-1;得知左右子樹的權(quán)值后,讓左右子樹權(quán)值進(jìn)行相加,把值賦給一個新的結(jié)點(diǎn)。 |
| 第四步 | 結(jié)構(gòu)體HTcode實(shí)例化一個對象begin,并定義兩個數(shù)組huffnode,huffcode,調(diào)用哈夫曼樹,并記錄編碼的位置為n-1;倒序編碼,并記錄下標(biāo)。然后回溯頭結(jié)點(diǎn),找到左孩子,賦值為0;找到右孩子賦值為1。然后進(jìn)行start–,倒著計算編碼,沿著父母結(jié)點(diǎn)往上走到結(jié)點(diǎn),保存并求出每個葉子節(jié)點(diǎn)的哈夫曼編碼與編碼的起始位。 |
| 第五步 | 定義數(shù)組code數(shù)組,字符指針q,輸入0和1的數(shù)字組合,此時哈夫曼樹對應(yīng)的葉子結(jié)點(diǎn)下標(biāo)為2n-2;然后進(jìn)行譯碼,如果讀到0,從根結(jié)點(diǎn)的左孩子繼續(xù)讀入,直到讀到1,然后繼續(xù)讀入,即讀到了一個葉子(字符),即翻譯一個字符成功,然后進(jìn)行遍歷輸出結(jié)果。 |
| 第六步 | 用戶選擇結(jié)束系統(tǒng) |
(三)詳細(xì)設(shè)計
1.實(shí)現(xiàn)思想
1.首先要構(gòu)造一棵哈夫曼樹,哈夫曼樹的結(jié)點(diǎn)結(jié)構(gòu)包括權(quán)值,雙親,左右孩子;假如由n個字符來構(gòu)造一棵哈夫曼樹,則共有結(jié)點(diǎn)2n-1個;在構(gòu)造前,先初始化,初始化操作是把雙親,左右孩子的下標(biāo)值都賦為0;然后依次輸入每個結(jié)點(diǎn)的權(quán)值
2.第二步是通過n-1次循環(huán),每次先找輸入的權(quán)值中最小的兩個結(jié)點(diǎn),把這兩個結(jié)點(diǎn)的權(quán)值相加賦給一個新結(jié)點(diǎn),,并且這個新結(jié)點(diǎn)的左孩子是權(quán)值最小的結(jié)點(diǎn),右孩子是權(quán)值第二小的結(jié)點(diǎn);鑒于上述找到的結(jié)點(diǎn)都是雙親為0的結(jié)點(diǎn),為了下次能正確尋找到剩下結(jié)點(diǎn)中權(quán)值 最小的兩個結(jié)點(diǎn),每次循環(huán)要把找的權(quán)值最小的兩個結(jié)點(diǎn)的雙親賦值不為0(i).就這樣通過n-1循環(huán)下、操作,創(chuàng)建了一棵哈夫曼樹,其中,前n個結(jié)點(diǎn)是葉子(輸入的字符結(jié)點(diǎn))后n-1個是度為2的結(jié)點(diǎn)
3.編碼的思想是逆序編碼,從葉子結(jié)點(diǎn)出發(fā),向上回溯,如果該結(jié)點(diǎn)是回溯到上一個結(jié)點(diǎn)的左孩子,則在記錄編碼的數(shù)組里存“0”,否則存“1”,注意是倒著存;直到遇到根結(jié)點(diǎn)(結(jié)點(diǎn)雙親為0),每一次循環(huán)編碼到根結(jié)點(diǎn),把編碼存在編碼表中,然后開始編碼下一個字符(葉子)
4.譯碼的思想是循環(huán)讀入一串哈夫曼序列,讀到“0”從根結(jié)點(diǎn)的左孩子繼續(xù)讀,讀到“1”從右孩子繼續(xù),如果讀到一個結(jié)點(diǎn)的左孩子和右孩子是否都為0,如果是說明已經(jīng)讀到了一個葉子(字符),翻譯一個字符成功,把該葉子結(jié)點(diǎn)代表的字符存在一個存儲翻譯字符的數(shù)組中,然后繼續(xù)從根結(jié)點(diǎn)開始讀,直到讀完這串哈夫曼序列,遇到結(jié)束符便退出翻譯循環(huán)
3.2 創(chuàng)建哈夫曼樹圖示
已知葉子節(jié)點(diǎn)為{10,20,30,40},以這4個權(quán)值構(gòu)建樹b的過程為:
3.實(shí)現(xiàn)流程圖
(四)代碼實(shí)現(xiàn)與測試
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<windows.h>#define Treecount 50 //最大葉結(jié)點(diǎn)個數(shù) #define MAXSIZE 500 typedef struct node {char data;int weight;int parent;int lchild;int rchild; } HTnode; typedef struct code {char data;int BT[MAXSIZE]; //數(shù)組,存放字符的哈夫曼編碼int start; //該編碼在數(shù)組中的開始位置char input;int num; } HTcode; void Createhufftree(HTnode HuffNode[],int n,HTcode array[]) { // array[]: 用來存儲字符及其權(quán)值int i,j;int minfirst,minsecond;int Lnode,Rnode;for(i=0; i<2*n-1; i++) {HuffNode[i].data=0;HuffNode[i].weight=0;HuffNode[i].parent=-1;HuffNode[i].lchild=-1;HuffNode[i].rchild=-1;}for(i=0; i<n-1; i++) {minfirst=minsecond=32767; //給最小和次小的樹賦最大值Lnode=Rnode=-1;for(j=0; j<n+i; j++) {if(HuffNode[j].parent==-1 && HuffNode[j].weight<minfirst) { //每次找出剩下的最小權(quán)值,并將最小權(quán)值賦給次小minfirst=minsecond;Rnode=Lnode;minfirst=HuffNode[j].weight;Lnode=j; //記錄下標(biāo)} else if(HuffNode[j].parent==-1 && HuffNode[j].weight<minsecond) {minsecond=HuffNode[j].weight;Rnode=j;}}HuffNode[Lnode].parent = n+i;HuffNode[Rnode].parent = n+i;HuffNode[n+i].weight = HuffNode[Lnode].weight+HuffNode[Rnode].weight;HuffNode[n+i].lchild = Lnode;HuffNode[n+i].rchild = Rnode;}for(i=0; i<n; i++) {HuffNode[i].weight=array[i].num; //將字符個數(shù)賦給權(quán)值HuffNode[i].data=array[i].input;} } void QQ() {printf("請輸入二進(jìn)制譯碼啟動密碼,密碼錯誤默認(rèn)結(jié)束(即為默認(rèn)退出):");long sercret;scanf("%d",&sercret);if(sercret==11011) {Sleep(1000);} else {Sleep(1000);printf("非法操作,結(jié)束系統(tǒng)");exit(-1);} } void HuffmanCoding(int n, HTcode array[]) {// array[]: 用來存儲字符及其權(quán)值HTnode HuffNode[1000];HTcode HuffCode[Treecount];HTcode begin;FILE *fp;fp=fopen("D:\\a.txt","w");Createhufftree(HuffNode,n,array);int i, j ;int loc,p;char code[30],*q;for(i=0; i<n; i++) {begin.start=n-1; //編碼的開始位置loc=i;p=HuffNode[loc].parent;while(p!=-1) {if(HuffNode[p].lchild==loc)begin.BT[begin.start]=0;elsebegin.BT[begin.start]=1;begin.start--; //倒著計算編碼loc=p; //沿著父母結(jié)點(diǎn)往上走到頂點(diǎn)p=HuffNode[loc].parent;}for(j=begin.start+1; j<n; j++)HuffCode[i].BT[j]=begin.BT[j];HuffCode[i].start=begin.start; // 保存求出的每個葉節(jié)點(diǎn)的哈夫曼編碼和編碼的起始位printf("\n");}printf("各個字符對應(yīng)的二進(jìn)制編碼(1//0//1//0)如下:\n");for(i=0; i<n; ++i) {printf("字符%c的密文:",HuffNode[i].data);for(j=HuffCode[i].start + 1; j<n; j++) //start走過了,加一恢復(fù)到開始位,編碼個數(shù)小于nprintf("%d", HuffCode[i].BT[j]);printf("\n");}printf("\n");printf("編碼成功,1s后打印哈夫曼編碼:\n");Sleep(1000);for(i=0; i<n; ++i) {for(j=HuffCode[i].start+1; j<n; j++) {fprintf(fp,"%d",HuffCode[i].BT[j]);}for(j=HuffCode[i].start + 1; j<n; j++) {printf("%d", HuffCode[i].BT[j]);}}printf("\n\n");if(i>0) {QQ();}// 譯碼:system("cls");printf(" ----- ∮哈夫曼譯碼系統(tǒng)已經(jīng)就緒 ∮------- \n");printf("各字符的對應(yīng)的編碼如下:\n"); for(i=0; i<n; ++i) {printf("字符%c的密文:",HuffNode[i].data);for(j=HuffCode[i].start + 1; j<n; j++) //start走過了,加一恢復(fù)到開始位,編碼個數(shù)小于nprintf("%d", HuffCode[i].BT[j]);printf("\n");}printf(" --> 請輸入對應(yīng)的二進(jìn)制數(shù)字: \n");scanf("%s",&code);q = code;loc = 2*n -2; // 哈夫曼樹的根節(jié)點(diǎn)下標(biāo)printf("譯碼成功,正在打印譯碼結(jié)果:\n");Sleep(2000);while( *q != NULL) {if( *q== '0') {loc = i =HuffNode[loc].lchild;if(HuffNode[loc].lchild == -1 && HuffNode[loc].rchild == -1) {printf("%c",HuffNode[i].data);fprintf(fp,"%c",HuffNode[i].data);loc = 2*n - 2;}} else if(*q == '1') {loc = i = HuffNode[loc].rchild;if(HuffNode[loc].lchild == -1 && HuffNode[loc].rchild == -1) {fprintf(fp,"%c",HuffNode[i].data);printf("%c",HuffNode[i].data);loc = 2*n-2; // 重新從根遍歷}} else {printf("非法輸入!");}q++;}printf("\n");fclose(fp); } void menu() {system("color F");printf(" ");printf(" \n");printf(" ____〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓____ \n");printf(" || 哈夫曼編碼 || \n");printf(" _________☆界面選擇如下☆_________ \n");printf(" \n");printf(" -> 1 ☆.編碼與譯碼 \n");printf(" -> 2 ☆.結(jié)束系統(tǒng) \n");printf("------------ ☆ ☆----------------- \n");printf(" ∮請手動輸入編號: "); } void Treeaction() {HTcode letter[50];char string[1000], *p;int i, account=0;printf(" ----- ∮哈夫曼編碼系統(tǒng)已經(jīng)就緒 ∮------- \n");printf(" 》》 請輸入你要編碼的字符串:");scanf("%s",&string);for(i=0; i<50; i++) {letter[i].input=0;letter[i].num=0;}p=string;while(*p!=NULL) {for(i=0; i<=account+1; i++) {if(letter[i].input==NULL) { //letter[i].input存儲所有的字符,用letter[i].num存儲相同的字符的數(shù)量letter[i].input=*p;letter[i].num++;account++; // 統(tǒng)計不同字符的個數(shù)break;} else if(letter[i].input==*p) {letter[i].num++;break;}}p++;}printf("\n");printf("不同的明文(字符)個數(shù):%d\n",account);printf("\n");printf("輸出輸入的字符的權(quán)值\n");for(i=0; i<account; i++) { //輸出不同字符及其所對應(yīng)的權(quán)值printf("當(dāng)前字符%c的權(quán)值為%d",letter[i].input,letter[i].num);printf("\n");}printf("\n");HuffmanCoding(account,letter);} int main() {int n,flaglist=1;char choice;while(flaglist) {menu();scanf("%d",&n);switch(n) {case 1:system("cls");Treeaction();printf("\n");printf("2秒后系統(tǒng)進(jìn)行返回!\n") ;Sleep(2000);printf("\n");system("cls");printf("已經(jīng)返回!\n"); printf("請根據(jù)個人意愿進(jìn)行操作!\n");printf("\n");break;case 2:exit(0);}}return 0; }(五)個人總結(jié)
通過這次課程設(shè)計,我收獲了很多,認(rèn)清了課程設(shè)計的需求,更認(rèn)清了自己。談起缺點(diǎn),自己代碼實(shí)現(xiàn)的能力也是破綻百出,對相對較難的題目不能冷靜解決,經(jīng)常犯一些低級的錯誤。然無獨(dú)有偶,自己也有優(yōu)點(diǎn),自己思想上能理解難題,并嘗試解決難題,考慮比較周到。這次的課程設(shè)計,難度處于中等地位,但實(shí)現(xiàn)起來并不沒有想象中的那么容易。它需要考慮多種因素,必須充分理解題目要求,化大難題為多個小難題,把具體實(shí)現(xiàn)步驟細(xì)分,認(rèn)真完成每一步步驟,然后綜合實(shí)現(xiàn)題目對應(yīng)的問題。 時間如白駒過隙,無聲滑過指尖。
學(xué)期即將完結(jié),即將大二,不免多生感想。回想一年時光,自己雖說付出努力,然并不能收益眾多。深知自己與他人之差距,然必須成為優(yōu)秀者,方可日后有一席之地。
我沒有科學(xué)家的那樣的天賦異稟,而我卻又有一顆積極向上的心,以“努力不一定成功,但不努力絕不會成功”為座右銘,付諸行動與汗水,不忘初心,厚積薄發(fā)。正如詩人王之渙所說:“欲窮千里目,更上一層樓。”要有“燕雀安知鴻鵠之志”的眼光,實(shí)現(xiàn)能力之飛躍,思想之迅捷,然自己必須十分努力,才能看起來毫不費(fèi)力。
風(fēng)雨過后才會見到彩虹。你盡管去努力,剩下的交給天命。定當(dāng)不忘學(xué)習(xí),砥礪前行。肺腑之言,溢于言表。
總結(jié)
以上是生活随笔為你收集整理的Huffman编码的设计与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 买余额宝会亏吗
- 下一篇: 新能源股票有哪些龙头股 可以优先关注这