C語言之霍夫曼編碼學習
1,霍夫曼編碼描述
哈夫曼樹─即最優二叉樹,帶權路徑長度最小的二叉樹,經常應用於資料壓縮。 在計算機資訊處理中,“哈夫曼編碼”是一種一致性編碼法(又稱“熵編碼法”),用於資料的無損耗壓縮。這一術語是指使用一張特殊的編碼表將源字元(例如某檔案中的一個符號)進行編碼。這張編碼表的特殊之處在於,它是根據每一個源字元出現的估算機率而建立起來的(出現機率高的字元使用較短的編碼,反之出現機率低的則使用較長的編碼,這便使編碼之後的字串的平均期望長度降低,從而達到無失真壓縮資料的目的)。這種方法是由David.A.Huffman發展起來的。 例如,在英文中,e的出現機率很高,而z的出現機率則最低。當利用哈夫曼編碼對一篇英文進行壓縮時,e極有可能用一個位(bit)來表示,而z則可能花去25個位(不是26)。用普通的表示方法時,每個英文字母均佔用一個位元組(byte),即8個位。二者相比,e使用了一般編碼的1/8的長度,z則使用了3倍多。若能實現對於英文中各個字母出現機率的較準確的估算,就可以大幅度提高無失真壓縮的比例。
2,問題描述
霍夫曼編碼前首先要統計每個字的字頻,即出現次數,例如:
1、將所有字母出現的次數以從小到大的順序排序,如上圖
2、每個字母都代表一個終端節點(葉節點),比較F.O.R.G.E.T五個字母中每個字母的出現頻率,將最小的兩個字母頻率相加合成一個新的節點。如上圖所示,發現F與O的頻率最小,故相加2+3=5,將F、O組成一個樹,F為左節點,O為右節點,(FO)為根節點,每個節點的取值為其出現頻率(FO的出現頻率為5)
3、比較5.R.G.E.T,發現R與G的頻率最小,故相加4+4=8,將RG組成一個新的節點
4、比較5.8.E.T,發現5與E的頻率最小,故相加5+5=10,因此將FO作為左節點,E作為右節點,FOE作為根節點
5、比較8.10.T,發現8與T的頻率最小,故相加8+7=15,將RG作為左節點,T作為右節點,RGT作為根節點
6、最後剩10.15,沒有可以比較的物件,相加10+15=25,FOE作為左節點,RGT作為右節點
根節點不取值,每個左子節點取值0,右子節點取值1,將每個字母從根節點開始遍歷,沿途的取值組成編碼:
首先選擇一個文字,統計每個字元出現的次數,組成以下陣列:
typedef struct FrequencyTreeNode {
int freq;
char c;
struct FrequencyTreeNode *left;
struct FrequencyTreeNode *right;
} FrequencyTreeNodeStruct, *pFrequencyTreeNodeStruct;
然後將獲得的陣列frequencies進行排序,按照freq由小到大的順序組成一個二叉查詢樹,FrequencyTreeNodeStruct,從二叉查詢樹中找到最小的節點,從樹中刪除,再取最小的節點,兩個子節點,組成一個新的樹,根節點c為0,freq為兩個子節點的和,加入frequencies中,並排序,重複該步驟,一直到frequencies中只有一個節點,則該節點為Huffman coding tree的根節點
以short型別按照前述的規則為每個字元編碼,爾後將文字翻譯為Huffman coding,再透過Huffman coding tree進行解碼,驗證編碼的正確性。
3,程式碼實現
-
#include<stdio.h>
-
#define n 5 //葉子數目
-
#define m (2*n-1) //結點總數
-
#define maxval 10000.0
-
#define maxsize 100 //哈夫曼編碼的最大位數
-
-
-
//定義結構體
-
typedef struct FrequencyTreeNode {
-
int freq;
-
char c;
-
struct FrequencyTreeNode *left;
-
struct FrequencyTreeNode *right;
-
} FrequencyTreeNodeStruct, *pFrequencyTreeNodeStruct;
-
-
-
FrequencyTreeNodeStruct frequencies[MAXALPABETNUM];
-
-
-
typedef struct
-
{
-
char bits[n]; //位串
-
int start; //編碼在位串中的起始位置
-
char ch; //字元
-
}codetype;
-
-
-
// 讀取檔案內容,統計字元以及出現頻率
-
void readTxtStatistics(char* fileName)
-
{
-
unsigned int nArray[52] = {0};
-
unsigned int i, j;
-
char szBuffer[MAXLINE];
-
int k=0;
-
// 讀取檔案內容
-
FILE* fp = fopen(fileName, \"r\");
-
if (fp != NULL)
-
{ /*讀取檔案內容,先統計字母以及出現次數*/
-
while(fgets(szBuffer, MAXLINE, fp)!=NULL)
-
{
-
for(i = 0; i < strlen(szBuffer); i++)
-
{
-
if(szBuffer[i] <= \'Z\' && szBuffer[i] >= \'A\')
-
{
-
j = szBuffer[i] - \'A\';
-
}
-
else if(szBuffer[i] <= \'z\' && szBuffer[i] >= \'a\')
-
{
-
j = szBuffer[i] - \'a\' + 26;
-
}
-
else
-
continue;
-
nArray[j]++;
-
}
-
}
-
-
-
// 然後賦值給frequencies陣列
-
for(i = 0, j = \'A\'; i < 52; i++, j++)
-
{
-
if (nArray[i] >0)
-
{
-
/*****/
-
frequencies[k].c=j;
-
frequencies[k].freq=nArray[i];
-
frequencies[k].left=NULL;
-
frequencies[k].right=NULL;
-
k++;
-
printf(\"%c:%d\\n\", j, nArray[i]);
-
}
-
if(j == \'Z\')
-
j = \'a\' - 1;
-
}
-
}
-
}
-
-
-
//建立哈夫曼樹
-
void huffMan(frequencies tree[]){
-
int i,j,p1,p2;//p1,p2分別記住每次合併時權值最小和次小的兩個根結點的下標
-
float small1,small2,f;
-
char c;
-
for(i=0;i<m;i++) //初始化
-
{
-
tree[i].parent=0;
-
tree[i].lchild=-1;
-
tree[i].rchild=-1;
-
tree[i].weight=0.0;
-
}
-
printf(\"【依次讀入前%d個結點的字元及權值(中間用空格隔開)】\\n\",n);
-
-
-
//讀入前n個結點的字元及權值
-
for(i=0;i<n;i++)
-
{
-
printf(\"輸入第%d個字元為和權值\",i+1);
-
scanf(\"%c %f\",&c,&f);
-
getchar();
-
tree[i].ch=c;
-
tree[i].weight=f;
-
}
-
//進行n-1次合併,產生n-1個新結點
-
for(i=n;i<m;i++)
-
{
-
p1=0;p2=0;
-
//maxval是float型別的最大值
-
small1=maxval;small2=maxval;
-
//選出兩個權值最小的根結點
-
for(j=0;j<i;j++)
-
{
-
if(tree[j].parent==0)
-
if(tree[j].weight<small1)
-
{
-
small2=small1; //改變最小權、次小權及對應的位置
-
small1=tree[j].weight;
-
p2=p1;
-
p1=j;
-
}
-
else if(tree[j].weight<small2)
-
{
-
small2=tree[j].weight; //改變次小權及位置
-
p2=j;
-
}
-
tree[p1].parent=i;
-
tree[p2].parent=i;
-
tree[i].lchild=p1; //最小權根結點是新結點的左孩子
-
tree[i].rchild=p2; //次小權根結點是新結點的右孩子
-
tree[i].weight=tree[p1].weight+tree[p2].weight;
-
}
-
}
-
}
-
-
-
//根據哈夫曼樹求出哈夫曼編碼,code[]為求出的哈夫曼編碼,tree[]為已知的哈夫曼樹
-
void huffmancode(codetype code[],frequencies tree[])
-
{
-
int i,c,p;
-
codetype cd; //緩衝變數
-
for(i=0;i<n;i++)
-
{
-
cd.start=n;
-
cd.ch=tree[i].ch;
-
c=i; //從葉結點出發向上回溯
-
p=tree[i].parent; //tree[p]是tree[i]的雙親
-
while(p!=0)
-
{
-
cd.start--;
-
if(tree[p].lchild==c)
-
cd.bits[cd.start]=\'0\'; //tree[i]是左子樹,生成程式碼\'0\'
-
else
-
cd.bits[cd.start]=\'1\'; //tree[i]是右子樹,生成程式碼\'1\'
-
c=p;
-
p=tree[p].parent;
-
}
-
code[i]=cd; //第i+1個字元的編碼存入code[i]
-
}
-
}
-
-
-
-
-
//根據哈夫曼樹解碼
-
void decode(hufmtree tree[])
-
{
-
int i,j=0;
-
char b[maxsize];
-
char endflag=\'2\'; //電文結束標誌取2
-
i=m-1; //從根結點開始往下搜尋
-
printf(\"輸入傳送的編碼(以\'2\'為結束標誌):\");
-
gets(b);
-
printf(\"編碼後的字元為\");
-
while(b[j]!=\'2\')
-
{
-
if(b[j]==\'0\')
-
i=tree[i].lchild; //走向左子節點
-
else
-
i=tree[i].rchild; //走向右子節點
-
if(tree[i].lchild==-1) //tree[i]是葉結點
-
{
-
printf(\"%c\",tree[i].ch);
-
i=m-1; //回到根結點
-
}
-
j++;
-
}
-
printf(\"\\n\");
-
if(tree[i].lchild!=-1&&b[j]!=\'2\') //文字讀完,但尚未到葉子結點
-
printf(\"\\nERROR\\n\"); //輸入文字有錯
-
}
-
-
-
-
-
void main()
-
{
-
printf(\"---------------—— 哈夫曼編碼實戰 ——\\n\");
-
printf(\"總共有%d個字元\\n\",n);
-
frequencies tree[m];
-
codetype code[n];
-
int i,j;//迴圈變數
-
huffMan(tree);//建立哈夫曼樹
-
huffmancode(code,tree);//根據哈夫曼樹求出哈夫曼編碼
-
printf(\"【輸出每個字元的哈夫曼編碼】\\n\");
-
for(i=0;i<n;i++)
-
{
-
printf(\"%c: \",code[i].ch);
-
for(j=code[i].start;j<n;j++)
-
printf(\"%c \",code[i].bits[j]);
-
printf(\"\\n\");
-
}
-
printf(\"【讀入內容,並進行編碼】\\n\");
-
// 開始編碼
-
decode(tree);
- }
----------------------------------------------------------------------------------------------------------------
有,文章允許轉載,但必須以連結方式註明源地址,否則追究法律責任!>
原部落格地址: http://blog.itpub.net/26230597/viewspace-1384144/
原作者:黃杉 (mchdba)
----------------------------------------------------------------------------------------------------------------
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30078170/viewspace-1396407/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C語言之家C語言
- x86彙編反編譯到c語言之——(2)if語句編譯C語言
- 學java語言之前學什麼利於學習?Java
- 演算法科普:有趣的霍夫曼編碼演算法
- C語言之基本概念C語言
- C語言之氣泡排序C語言排序
- C語言之結構體C語言結構體
- C語言之詭異字串C語言字串
- C語言之環形佇列C語言佇列
- C語言之字串與指標C語言字串指標
- C語言之static關鍵字C語言
- C語言之輸入輸出C語言
- 編譯語言、解釋語言與指令碼語言之間的區別編譯指令碼
- 從開源專案學習 C 語言基本的編碼規則
- x86彙編反編譯到c語言之——(1)表示式求值及賦值語句編譯C語言賦值
- C語言學習方法,怎麼學習C語言?C語言
- C語言之位段使用詳解C語言
- C語言之標準檔案操作C語言
- C語言之雙向連結串列C語言
- C語言之單向連結串列C語言
- C語言之字串處理函式C語言字串函式
- 深度學習中的函式語言之美 - Clojure和Haskell深度學習函式Haskell
- C 語言之布林型別介紹型別
- C語言之動態記憶體管理C語言記憶體
- c語言之解釋複雜指標C語言指標
- iOS開發系列--C語言之指標iOSC語言指標
- c語言之字串轉大寫聯絡C語言字串
- C語言之高階資料講解C語言
- C#編碼習慣C#
- Python語言和R語言之間有什麼不同?Python學習教程PythonR語言
- C語言再學習C語言
- C語言學習心得C語言
- C語言的學習C語言
- 讓賈伯斯立足肩上的C語言之父C語言
- iOS開發系列--C語言之預處理iOSC語言
- 【精】C語言之變數儲存型別C語言變數型別
- iOS開發系列--C語言之構造型別iOSC語言型別
- Java之父評價C語言之父:C語言撐起了一切JavaC語言