【算法学习笔记】哈夫曼树的构建和哈夫曼编码的实现代码
介紹
哈夫曼(Haffman)這種方法的基本思想如下:
①由給定的n個(gè)權(quán)值{W1,W2,…,Wn}構(gòu)造n棵只有一個(gè)葉子結(jié)點(diǎn)的二叉樹,從而得到一個(gè)二叉樹的集合F={T1,T2,…,Tn}。
②在F中選取根結(jié)點(diǎn)的權(quán)值最小和次小的兩棵二叉樹作為左、右子樹構(gòu)造一棵新的二叉樹,這棵新的二叉樹根結(jié)點(diǎn)的權(quán)值為其左、右子樹根結(jié)點(diǎn)權(quán)值之和。
③在集合F中刪除作為左、右子樹的兩棵二叉樹,并將新建立的二叉樹加入到集合F中。
④重復(fù)②、③兩步,當(dāng)F中只剩下一棵二叉樹時(shí),這棵二叉樹便是所要建立的哈夫曼樹。
對(duì)于同一組給定葉子結(jié)點(diǎn)所構(gòu)造的哈夫曼樹,樹的形狀可能不同,但帶權(quán)路徑長(zhǎng)度值是相同的,一定是最小的。
哈夫曼樹的構(gòu)建
為了方便操作,用靜態(tài)鏈表作為哈夫曼樹的存儲(chǔ)。 在構(gòu)造哈夫曼樹時(shí),設(shè)置一個(gè)結(jié)構(gòu)數(shù)組HuffNode保存哈夫曼樹中各結(jié)點(diǎn)的信息,根據(jù)二叉樹的性質(zhì)可知,具有n個(gè)葉子結(jié)點(diǎn)的哈夫曼樹共有2n-1個(gè)結(jié)點(diǎn),所以數(shù)組HuffNode的大小設(shè)置為2n-1,結(jié)點(diǎn)的結(jié)構(gòu)形式如下:
weight lchild rchild parent
其中,weight域保存結(jié)點(diǎn)的權(quán)值,lchild和rchild域分別保存該結(jié)點(diǎn)的左、右孩子結(jié)點(diǎn)在數(shù)組HuffNode中的序號(hào),從而建立起結(jié)點(diǎn)之間的關(guān)系。為了判定一個(gè)結(jié)點(diǎn)是否已加入到要建立的哈夫曼樹中,可通過parent域的值來確定。初始時(shí)parent的值為-1,當(dāng)結(jié)點(diǎn)加入到樹中時(shí),該結(jié)點(diǎn)parent的值為其雙親結(jié)點(diǎn)在數(shù)組HuffNode中的序號(hào),就不會(huì)是-1了。 構(gòu)造哈夫曼樹時(shí),首先將由n個(gè)字符形成的n個(gè)葉子結(jié)點(diǎn)存放到數(shù)組HuffNode的前n個(gè)分量中,然后根據(jù)前面介紹的哈夫曼方法的基本思想,不斷將兩個(gè)較小的子樹合并為一個(gè)較大的子樹,每次構(gòu)成的新子樹的根結(jié)點(diǎn)順序放到HuffNode數(shù)組中的前n個(gè)分量的后面。
代碼:
#define maxvalue 1e6 //定義最大權(quán)值整數(shù)常量 #define maxleaf 1e3 //定義哈夫曼樹中結(jié)點(diǎn)個(gè)數(shù)整數(shù)常量 #define maxnode maxleaf*2-1 typedef struct{ int weight; int parent; int lchild; int rchild; }HNodeType; HNodeType* HuffTree(){ HNodeType node[maxnode]; int i,j,n; int m1,m2,x1,x2; cin>>n;//輸入葉子結(jié)點(diǎn)個(gè)數(shù) for(int i = 0;i < n;i++) { node[i].weight=0; node[i].parent=-1; node[i].lchild=-1; node[i].rchild=-1;//初始化結(jié)點(diǎn) } for(int i = 0;i < n;i++) { cin>>node[i].weight;//輸入n個(gè)葉子結(jié)點(diǎn)的權(quán)值 } for(int i = 0;i <n-1;i++){ m1=m2=maxvalue;//注意由于需要最小的和次小的兩個(gè)權(quán)值,因此需要設(shè)兩個(gè)變量 x1=x2=0;//一共n-1個(gè)葉節(jié)點(diǎn),一共2n-1個(gè)結(jié)點(diǎn) for(int j=0;j<n+i;j++){ //構(gòu)造哈夫曼樹if(node[j].weight<m1&&node[j].parent=-1){ m2=m1; m1=node[j].weight; x2=x1;//保存結(jié)點(diǎn)的下標(biāo) x1=j;} else if(node[j].weight<m2&&node[j].parent=-1){ m2=node[j].weight; x2=j; } } //當(dāng)結(jié)點(diǎn)加入到樹中時(shí),該結(jié)點(diǎn)parent的值為其雙親結(jié)點(diǎn)在數(shù)組HuffNode中的序號(hào),就不會(huì)是-1了 //現(xiàn)在合并兩棵子樹 步驟:更新兩棵子樹的父節(jié)點(diǎn),修改父節(jié)點(diǎn)的權(quán)值,修改父節(jié)點(diǎn)的左右子樹信息 node[x1].parent=n+i; node[x2].parent=n+i;//最小權(quán)值的結(jié)點(diǎn)和倒數(shù)第二小的權(quán)值結(jié)點(diǎn)的雙親相同 node[n+i].weight=node[x1].weight+node[x2].weight;//記得更新父結(jié)點(diǎn)的權(quán)值 node[n+i].lchild=x1;//修改父節(jié)點(diǎn)的子樹 node[n+i].rchild=x2; } return node; }哈夫曼編碼
構(gòu)造編碼的時(shí)候人們希望解決的兩個(gè)問題是:
①編碼總長(zhǎng)最短。
②譯碼的唯一性。 哈夫曼樹可用于構(gòu)造使電文的編碼總長(zhǎng)最短的編碼方案。
具體做法如下:設(shè)需要編碼的字符集合為{d1,d2,…,dn},它們?cè)陔娢闹谐霈F(xiàn)的次數(shù)或頻率集合為{w1,w2,…,wn},以d1,d2,…,dn作為葉子結(jié)點(diǎn),w1,w2,…,wn作為它們的權(quán)值,構(gòu)造一棵哈夫曼樹,規(guī)定哈夫曼樹中的左分支代表0,右分支代表1,則從根結(jié)點(diǎn)到每個(gè)葉子結(jié)點(diǎn)所經(jīng)過的路徑分支組成的0和1的序列便為該結(jié)點(diǎn)對(duì)應(yīng)字符的編碼,稱為哈夫曼編碼。
實(shí)現(xiàn)哈夫曼編碼的算法可分為兩大部分:
①構(gòu)造哈夫曼樹。
②在哈夫曼樹上求葉結(jié)點(diǎn)的編碼。
求哈夫曼編碼,實(shí)質(zhì)上就是在已建立的哈夫曼樹中,從葉子結(jié)點(diǎn)開始,沿結(jié)點(diǎn)的雙親鏈域退回到根結(jié)點(diǎn),每退回一步,就走過了哈夫曼樹的一個(gè)分支,從而得到一位哈夫曼碼值。由于一個(gè)字符的哈夫曼編碼是從根結(jié)點(diǎn)到相應(yīng)葉子結(jié)點(diǎn)所經(jīng)過的路徑上各分支所組成的0、1序列,因此先得到的分支代碼為所求編碼的低位碼,后得到的分支代碼為所求編碼的高位碼。可以設(shè)置一結(jié)構(gòu)數(shù)組HuffCode用來存儲(chǔ)各字符的哈夫曼編碼信息,數(shù)組元素的結(jié)構(gòu)如下:
bit start
其中,分量bit為一維數(shù)組,用來保存字符的哈夫曼編碼,start表示該編碼在數(shù)組bit中的開始位置。所以,對(duì)于第i個(gè)字符,它的哈夫曼編碼存放在HuffCode[i].bit中的從HuffCode[i].start到n的分量上。
算法實(shí)現(xiàn);(寫法一)
寫法二:
typedef HuffNode{ char data;//待編碼的符號(hào) double weight;//符號(hào)出現(xiàn)的頻率 int parent,lchild,rchild; }HTnode,*HuffmanTree;編碼:從葉結(jié)點(diǎn)回退,左分支記0 右記1
void Code(HuffmanTree &HT,int n,int i,char *code) { //求第i個(gè)字符的編碼 int p,parent,start; char *cd; cd = new char[n]; cd[n-1] = '\0'; start = n-1; p = i;//p是當(dāng)前結(jié)點(diǎn)的下標(biāo) parent = HT[i].parent;//當(dāng)前結(jié)點(diǎn)的父節(jié)點(diǎn)下標(biāo) while(parent != -1) { if(HT[parent].lchild == p) cd[--start] = '0'; else cd[--start] = '1'; p = parent; parent = HT[patent].parent;//沿雙親回退 } strcpy(code,&cd[start]); delete[]cd; }總結(jié)
以上是生活随笔為你收集整理的【算法学习笔记】哈夫曼树的构建和哈夫曼编码的实现代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【算法学习笔记】二叉树的基本操作实现和应
- 下一篇: 【python网络编程】创建TCP/UD