赫夫曼樹的構建、編碼、譯碼解析
當你開始看這篇博文的時候。我相信你對樹及二叉樹的基本概念已有所了解。我在這裏就不再贅述。
我們主要對赫
夫曼樹的特點、構建、編碼、譯碼做一個具體的介紹,並附有代碼,全部函數代碼都通過了測試。我不保證全部代碼是最優的(畢竟是我一個人苦思冥想出來的,我相信在大家的集思廣益之下還有優化的空間),但我保證全部代碼是正確的。
一、赫夫曼樹的特點
赫夫曼樹又稱作最優二叉樹,是一類帶權路徑長度最短的樹。
首先給出路徑和路徑長度的概念。從樹中一個節點到
還有一個節點之間的分支構成這兩個節點之間的路徑。路徑上的分支數目稱作路徑長度。樹的路徑長度是從樹根到每個葉子節點的路徑長度之和。
節點的帶全路徑長度
所謂的最優二叉樹就是WPL的值最小。
二、赫夫曼樹的構造方法
赫夫曼最早給出了一個帶有一般規律的算法,俗稱赫夫曼算法。例如以下:
為Wi的根節點。其左右子樹都為空。①給定n個權值{w1。w2,...。wn}。構成n棵二叉樹的集合T={T1,T2......。Tn},當中每棵二叉樹Ti僅僅有一個權
②在T中選取兩棵根節點權值最小的樹做為左右子樹構造一棵新的二叉樹。且置新樹的根節點的權值為其左右孩
子的樹上的根節點權值之和。
③在T中刪除這兩棵樹,並將新得到的二叉樹增加到T中。
④反復②、③步驟,直到T中僅僅有一個樹為止。這棵樹就是赫夫曼樹。
構造的詳細步驟例如以下:
三、赫夫曼樹構造、編碼、譯碼的思路
在構造赫夫曼樹的時候,我們須要一個鏈表,當給我們n個keyword及其所相應的權值的時候,先構造一個赫夫曼
樹節點。然後將赫夫曼樹節點做為鏈表節點的數據域插入到鏈表中(這個鏈表的插入操作是依照樹根節點的權值大小排好序的),再構造剩余的n-1個赫夫曼樹節點,並插入到鏈表中,當完畢插入工作後。我們的有序鏈表也就構造出來了。然後對有序鏈表中的節點完畢合並、刪除、插入,直到鏈表中僅僅有一個節點位置,這個節點的數據域就是指向赫夫曼樹根節點的指針。
當有了赫夫曼樹之後,我們對其進行編碼,在編碼的時候左分支為0,右分支為1,例如以下圖所看到的。以先序遍歷二叉
樹,然後還要定義一整數a,初始值為1。在訪問某一節點時將其作為參數傳入,假設訪問的是左節點傳入a*2,訪問右節點傳入a*2+1,也就是在a的二進制數據中,向左走在末尾加個0,向右走加個1(初始化為1是為了避免開始時向左走。無法加零的情況,輸出時要將首位的1去掉),訪問到葉子節點時將a轉化成二進制,再將首位的1去掉就可以。
孩子往下走;直到走到葉子節點為止。假設報文中還有數據則接著從赫夫曼樹的根節點出發查找。赫夫曼譯碼:我們從報文中取出二進制,假設為0,則沿著根節點的左孩子往下走;假設為1。則沿著根節點的右
假設在查找的過程中無法找到葉子節點,則報文有錯誤。
四、赫夫曼樹構造、編碼、譯碼代碼例如以下
1、我們須要兩個結構體,一個是赫夫曼樹節點的結構體。還有一個是鏈表節點的結構體:
typedef struct Node { int weight; key_type key; struct Node *lchild, *rchild; }HFMNode,*HFMTree; /*用於鏈表的結構體*/ typedef struct link_node { HFMTree data; struct link_node *next; }LNode,*Link;2、用到的函數例如以下:
void init_link(Link *head);//初始化鏈表
void insert_link(Link head, HFMTree hfm);//向鏈表中插入一個元素,並依照權重排序
int delete_link(Link head,HFMTree *hfm);//依次刪除鏈表中的數據。成功返回1,失敗返回0
/*創建赫夫曼樹,str為keyword,w為相應的權重*/
int creat_hfmTree(HFMTree *root,char str[],int w[]);
/*獲取赫夫曼編碼表,存儲在數組code中*/
void hfmTree_code(HFMTree head, int a,char code[]);
/*譯碼,譯碼結果存儲在decode數組中,code輸入的報文*/
int hfmTree_decode(HFMTree head,const char code[],char decode[]);
3、總體代碼例如以下:
#include <iostream> #include <assert.h> typedef char key_type; /*用於赫夫曼樹的結構體*/ using namespace std; typedef struct Node { int weight; key_type key; struct Node *lchild, *rchild; }HFMNode,*HFMTree; /*用於鏈表的結構體*/ typedef struct link_node { HFMTree data; struct link_node *next; }LNode,*Link; /*對鏈表的操作*/ void init_link(Link *head);//初始化鏈表 void insert_link(Link head, HFMTree hfm);//向鏈表中插入一個元素。並依照權重排序 int delete_link(Link head,HFMTree *hfm);//依次刪除鏈表中的數據,成功返回1。失敗返回0 /*創建赫夫曼樹,str為keyword,w為相應的權重*/ int creat_hfmTree(HFMTree *root,char str[],int w[]); /*獲取赫夫曼編碼表,存儲在數組code中*/ void hfmTree_code(HFMTree head, int a,char code[]); /*譯碼,譯碼結果存儲在decode數組中,code輸入的報文*/ int hfmTree_decode(HFMTree head,const char code[],char decode[]); int main() { HFMTree root; char str[] = "abcdefg"; int w[] = { 12, 18, 36, 79,85,32,40}; creat_hfmTree(&root, str, w); char code[1024] = { 0 };//用來存放編碼表 hfmTree_code(root, 1,code); cout <<"編碼結果為:"<<endl<< code << endl; char decode[90] = { 0 }; hfmTree_decode(root, "00001100010010100111011", decode); cout <<"譯碼結果為:"<<endl<< decode << endl; } void init_link(Link *head) { (*head) = (Link)malloc(sizeof(LNode)); (*head)->next = NULL; (*head)->data = NULL; } void insert_link(Link head, HFMTree hfm) { assert(head != NULL); /*先構造鏈表節點*/ Link temp = (Link)malloc(sizeof(LNode)); temp->next = NULL; temp->data = hfm; while (head->next != NULL && head->next->data->weight < hfm->weight) { head = head->next; } if (head->next == NULL) { head->next = temp; } else { temp->next = head->next; head->next = temp; } } int delete_link(Link head, HFMTree *hfm) { assert(head != NULL); if (head->next == NULL) return 0; Link temp = head->next; head->next = temp->next; *hfm = temp->data; free(temp); return 1; } int creat_hfmTree(HFMTree *root, char str[], int w[]) { int n = strlen(str); HFMTree temp; Link head; init_link(&head); /*構造節點,依照權重的遞增順序插入到鏈表中*/ for (int i = 0; i < n;i++) { /*構造節點*/ temp = (HFMTree)malloc(sizeof(HFMNode)); temp->key = str[i]; temp->weight = w[i]; temp->lchild = NULL; temp->rchild = NULL; insert_link(head, temp); } if (head->next == NULL)//鏈表中沒有元素 return 0; /*從鏈表中取出兩個最小的節點構造赫夫曼樹*/ HFMTree temp1, temp2, temp3; /*鏈表中存在1個元素則退出循環*/ while ( delete_link(head,&temp1) && delete_link(head,&temp2) ) { temp3 = (HFMTree)malloc(sizeof(HFMNode)); temp3->weight = temp1->weight + temp2->weight; temp3->lchild = temp1; temp3->rchild = temp2; insert_link(head, temp3); } *root = temp1; return 1; } void hfmTree_code(HFMTree head, int a, char code[]) { static int i = 0; if (head != NULL) { if (head->lchild == NULL && head->rchild == NULL)//訪問到了葉子節點 { code[i++] = head->key; code[i++] = ':';//在keyword後面加":",其後面的"0"、"1"表示編碼 int count = 0; /*對a做處理。a最前面的1要舍去*/ while (a > 1) { if (a % 2 == 0) code[i++] = '0'; else code[i++] = '1'; a = a>>1; count++; } /*對字符串反轉,這樣才和我們的赫夫曼編碼一直*/ strrev( code + i - count); /*keyword之間的編碼用空格分開*/ code[i++] = ' '; } hfmTree_code(head->lchild, a * 2, code); hfmTree_code(head->rchild, a * 2 + 1, code); } } int hfmTree_decode(HFMTree head, const char code[], char decode[]) { assert(head != NULL); HFMTree root = head,pre; int i = 0; int j = 0; while (code[i] == '0' || code[i] == '1') { if (code[i] == '0') head = head->lchild; else head = head->rchild; if (head->rchild == NULL )//訪問到了葉子節點 { decode[j] = head->key; j++; head = root; } i++; } if (code[i] != '\0')//沒有訪問到葉子節點 { cout << "報文錯誤" << endl; return 0; } return 1; }輸出結果為:
赫夫曼樹的構建、編碼、譯碼解析