最近在翻炒一些關於樹的知識,發現一個比較有意思的二叉樹,huffman樹,對應到離散數學中的一種名為最優二叉樹的路徑結構,而Huffman的主要作用,最終可以歸結到一種名為huffman編碼的編碼方式,使用huffman編碼方式,我們可以以平均長度最短的碼字來記錄一串信息,且每個信息分子的編碼唯一,獨立。從而最終合成編碼所對應的信息唯一,無歧義。
huffman樹的創建時基於每個信息分子都擁有其權重,權重越大,越靠近樹根,即路徑越短,
下面我們我們來以一個huffman樹的例子為例:簡單引入一下huffman樹:
上圖即是構造huffman編碼所必備的元素,那么,通過上圖中的信息分子與對應編碼,我們就可以寫出編碼解析結果唯一且無歧義的編碼串 如:
001011110100111 CDGFH
000111110010 CHEF
huffman編碼的任何組合方式都只可能對應一串信息,不可能有歧義出現。 因為在huffman樹中,每一個信息元都是一個葉子節點。。。
期huffman樹形結果為:
圓圈中的數字表示權重,我們規定向左為0 向右為1 ,, 即的到上面表格中的huffman編碼,通過上圖中的樹,我們不難算出樹的權
W(T)為291651 必為所有的由這些信息元組合成的樹中的權的最小值,當然,組合方式有可能不一樣,但最終的權,只會大於或等於他,即不存在與權值相等且為最小權值的非同構的兩顆樹。
如何創建這個huffman樹(最優二叉樹)呢,這才是我們今天的關鍵。
首先我們需要明確一樣東西,基於信息元所創建的huffman樹的節點個數是否確定,答案是肯定的,如果在一開始我們所要創建的數據結構的長度是確定的話,那么我覺得我們有很大的必要選擇數組了。
數組的長度:m = 信息元個數n * 2 - 1; 即我們需要n個單元存放信息元節點,n-1的單元來存放分支點(內點和根節點)。
數組單元的數據結構(不考慮信息元數據):
由於樹的存儲結構是用數組實現的,故parent,rchild,lchild中直接保存數組下標即可。
一切都具備好了,那哥們兒幾個就來初始化一下這棵樹把(以上面的例子為例):
(初始狀態,還未進行建樹):
建樹動作完成之后:
咦,中間的步驟哪里去了呢??? 別急!!!
聽我說: 1:尋找數組中單元數據的parent不為零的兩個數組元c1 , c2
2:找到他們的父節點father,父節點:數組index遞增序列中第一個weight為零的數組元(前提:信息元中不存在weight為零的權)。
3:將父節點的lchild指向c1 和 c2中序號(index)在前面的那個數組單元(即lchild = indexMin(c1,c2).index), rchild則等於另一個的index,將c1和c2的parent都指向找到的父節點,即c1/c2.parent = father.index。同時c1和c2的權之和賦給father的weight(權)
4:重復1,2,3,直到左右數組單元的weight都被數據化(賦值)。
代碼如下:
void HuffmanCoding(HuffmanTree *HT,int *w,int n) { /*w為權值數組,n為信息元個數*/ int m,i,s1,s2; HuffmanTree p; char *cd; if(n<=1) return; m=2*n-1; *HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); /* 0號單元未用 */ for(p=*HT+1,i=1;i<=n;++i,++p,++w) //parent lcahid rchild全部初始化為零 { (*p).weight=*w; (*p).parent=0; (*p).lchild=0; (*p).rchild=0; } for(;i<=m;++i,++p) (*p).parent=0; for(i=n+1;i<=m;++i) /* 建赫夫曼樹 */ { /* 在HT[1~i-1]中選擇parent為0且weight最小的兩個結點,其序號分別為s1和s2 */ select(*HT,i-1,&s1,&s2); (*HT)[s1].parent=(*HT)[s2].parent=i; (*HT)[i].lchild=s1; (*HT)[i].rchild=s2; (*HT)[i].weight=(*HT)[s1].weight+(*HT)[s2].weight; } }
那么,最終的huffman編碼如何實現,以及我們如何起實現反編碼(從編碼得到信息),筆者將會最今后的日子里進行探討(沒時間啦啦),阿里亞瑟哦,覺得不錯的話,記得點贊哦。