使用優先隊列構建赫夫曼樹
阿新 • • 發佈:2017-05-19
tex encode b2c fontsize 結構 技術分享 abi enter row
關於赫夫曼編碼和赫夫曼樹的相關知識可參考之前兩篇文章(由二叉樹構造赫夫曼樹、赫夫曼編碼)。
步驟: 1.首先我們須要統計不同字符出現的次數。一個字符出現的次數越多,說明其優先級越高,其赫夫曼編碼應該越短; 2.將待編碼的字符(即帶權節點)存入優先級隊列,優先級即字符出現的次數; 3.不斷叠代隊列,直到隊列中剩下一個元素(即根節點)。每次從隊列中取出兩個優先級最小的元素(優先級隊列中的元素是依照節點優先級順序排列的),然後生成一個新的赫夫曼樹節點,節點左右孩子分別指向這兩個元素,節點權值為這兩個元素權值之和。將新節點插入隊列; 4.依據赫夫曼樹生成赫夫曼編碼表。遞歸遍歷赫夫曼樹,對每一個葉子節點進行編碼(左0右1); 5.編碼過程即對每一個字符查編碼表的過程; 6.解碼過程即依據編碼串訪問赫夫曼樹的過程.
實現:
測試結果:
優先隊列變化過程:
生成的赫夫曼樹:
註:這裏的構建赫夫曼樹的約定和之前的兩篇不太一樣,這裏約定一個節點的左節點的權值小於右節點的權值.
本文介紹還有一種構建赫夫曼樹的方式,採用優先隊列.
步驟: 1.首先我們須要統計不同字符出現的次數。一個字符出現的次數越多,說明其優先級越高,其赫夫曼編碼應該越短; 2.將待編碼的字符(即帶權節點)存入優先級隊列,優先級即字符出現的次數; 3.不斷叠代隊列,直到隊列中剩下一個元素(即根節點)。每次從隊列中取出兩個優先級最小的元素(優先級隊列中的元素是依照節點優先級順序排列的),然後生成一個新的赫夫曼樹節點,節點左右孩子分別指向這兩個元素,節點權值為這兩個元素權值之和。將新節點插入隊列; 4.依據赫夫曼樹生成赫夫曼編碼表。遞歸遍歷赫夫曼樹,對每一個葉子節點進行編碼(左0右1); 5.編碼過程即對每一個字符查編碼表的過程; 6.解碼過程即依據編碼串訪問赫夫曼樹的過程.
實現:
/***************************************************** 赫夫曼編碼優先隊列版 by Rowandjj 2014/6/17 *****************************************************/ #include<iostream> using namespace std; typedef struct _HTNODE_//赫夫曼樹節點的存儲結構 { char symbol;//符號(ASC碼值) struct _HTNODE_ *left;//左孩子 struct _HTNODE_ *right;//右孩子 }HtNode; typedef struct _HTREE_//赫夫曼樹 { HtNode *root; }HTree; typedef struct _HLNODE_//赫夫曼碼表節點 { char symbol;//ASC碼值 char *code;//相應的編碼 struct _HLNODE_ *next; }HlNode; typedef struct _HLTABLE_//赫夫曼碼表(以單鏈表形式存儲不同ASC碼及與之相應的編碼表) { HlNode *first; HlNode *last; }HlTable; //-------------------------------------------------------- #define MAX_SZ 256//隊列最大長度 #define TYPE HtNode*//優先隊列的節點定義 typedef struct _PQUEUENODE_//隊列節點定義 { TYPE val;//節點值 unsigned int priority;//優先級 struct _PQUEUENODE_ *next; }pQueueNode; typedef struct _PQUEUE_//優先隊列定義 { unsigned int size;//隊列長度 pQueueNode *first;//首節點 }pQueue; //-----------優先隊列操作定義------------------------------ void InitQueue(pQueue **queue);//隊列初始化 void AddQueue(pQueue **queue,TYPE val,unsigned int priority);//將一個帶優先級的節點插入隊列 TYPE GetQueue(pQueue **queue);//隊列節點依照優先級順序出隊 void TraverseQueue(pQueue *queue);//遍歷隊列 //-----------赫夫曼編碼操作定義---------------------------- HTree* BuildTree(char *inputString);//創建赫夫曼樹 HlTable* BuildTable(HTree *huffmanTree);//構建赫夫曼碼表(出現頻率最高的ASC碼將使用最短的編碼) void encode(HlTable *table,char *stringToEncode);//編碼 void decode(HTree *tree,char *stringToDecode);//解碼 void TraverseTable(HlTable *table);//遍歷赫夫曼編碼表 //--------------------------------------------------------- void InitQueue(pQueue **queue) { *queue = (pQueue *)malloc(sizeof(pQueue)); (*queue)->first = NULL; (*queue)->size = 0; } void AddQueue(pQueue **queue,TYPE val,unsigned int priority) { if((*queue)->size == MAX_SZ) { cout<<"queue is full!"; return; } pQueueNode *aux = (pQueueNode*)malloc(sizeof(pQueueNode)); aux->priority = priority; aux->val = val; aux->next = NULL; if((*queue)->first == NULL)//生成隊列首節點 { (*queue)->first = aux; (*queue)->size++; }else { if(priority <= (*queue)->first->priority)//插到首節點位置 { aux->next = (*queue)->first; (*queue)->first = aux; (*queue)->size++; }else//遍歷隊列插到合適的位置 { pQueueNode *iterator = (*queue)->first; while(iterator->next != NULL && priority > iterator->next->priority) { iterator = iterator->next; } aux->next = iterator->next; iterator->next = aux; (*queue)->size++; } } } TYPE GetQueue(pQueue **queue) { TYPE returnVal = NULL; if ((*queue)->size > 0) { pQueueNode *nodeToDel = (*queue)->first; (*queue)->first = (*queue)->first->next; returnVal = nodeToDel->val; free(nodeToDel); (*queue)->size--; }else { cout<<"queue is empty"<<endl; } return returnVal; } //------------------------------------------------------------- HTree* BuildTree(char *inputString) { //統計字符出現次數 int *probability = (int*)malloc(sizeof(int)*256); int i; for(i = 0; i < 256;i++) { probability[i] = 0; } for(i = 0; inputString[i] != ‘\0‘;i++) { probability[(unsigned char)inputString[i]]++; } /* for(i = 0; i < 256; i++)//打印字符出現的次數(也即頻率) { if(probability[i] != 0) cout<<(char)i<<":"<<probability[i]<<endl; } */ //依據每一個字符出現的頻率生成赫夫曼樹 pQueue* huffmanQueue; InitQueue(&huffmanQueue); for(i = 0; i < 256; i++) { //1 現將帶權節點加到隊列中 if(probability[i] != 0)//僅僅考慮帶權節點,也就是說僅僅對帶權節點編碼 { HtNode *aux = (HtNode*)malloc(sizeof(HtNode)); aux->symbol = (char)i; aux->left = NULL; aux->right = NULL; AddQueue(&huffmanQueue,aux,probability[i]); } } cout<<"字符及出現頻次:"<<endl; TraverseQueue(huffmanQueue); free(probability); //2 再構建赫夫曼樹 while(huffmanQueue->size != 1) { int priority = huffmanQueue->first->priority; priority += huffmanQueue->first->next->priority; //從隊列中獲取兩個優先級最低的節點 HtNode *left = GetQueue(&huffmanQueue); HtNode *right = GetQueue(&huffmanQueue); //生成其父節點,父節點的權值為兩個子節點的權值之和 HtNode *newNode = (HtNode*)malloc(sizeof(HtNode)); newNode->left = left; newNode->right = right; //將生成的父節點插入到優先隊列的合適位置 AddQueue(&huffmanQueue,newNode,priority); } HTree *tree = (HTree*)malloc(sizeof(HTree)); tree->root = GetQueue(&huffmanQueue); return tree; } void travelseTree(HtNode *treeNode,HlTable **table,int k,char code[256]) { if(treeNode->left == NULL && treeNode->right == NULL)//走到葉子節點 { code[k] = ‘\0‘; HlNode *aux = (HlNode*)malloc(sizeof(HlNode)); aux->symbol = treeNode->symbol; aux->code = (char *)malloc(sizeof(char)*(strlen(code)+1)); strcpy(aux->code,code); aux->next = NULL; if((*table)->first == NULL)//連接 { (*table)->first = (*table)->last = aux; }else { (*table)->last->next = aux; (*table)->last = aux; } } if(treeNode->left != NULL) { code[k] = ‘0‘; travelseTree(treeNode->left,table,k+1,code); } if(treeNode->right != NULL) { code[k] = ‘1‘; travelseTree(treeNode->right,table,k+1,code); } } HlTable* BuildTable(HTree *huffmanTree)//由赫夫曼樹構建赫夫曼編碼表 { HlTable *table = (HlTable*)malloc(sizeof(HlTable)); table->first = NULL; table->last = NULL; char code[256]; int k = 0; travelseTree(huffmanTree->root,&table,k,code); return table; } void encode(HlTable *table,char *stringToEncode) { HlNode *travel; for(int i = 0; stringToEncode[i] != ‘\0‘; i++) { travel = table->first; while(travel->symbol != stringToEncode[i]) { travel = travel->next; } cout<<travel->code<<" "; } } void decode(HTree *tree,char *stringToDecode) { HtNode *travel = tree->root; for(int i = 0;stringToDecode[i] != ‘\0‘; i++) { if(travel->left == NULL && travel->right == NULL) { cout<<travel->symbol; travel = tree->root; } if(stringToDecode[i] == ‘0‘) { travel = travel->left; } if(stringToDecode[i] == ‘1‘) { travel = travel->right; } if(stringToDecode[i] != ‘0‘ && stringToDecode[i] != ‘1‘) { cout<<"\ndecode error..."<<endl; } } if(travel->left == NULL && travel->right == NULL) { cout<<travel->symbol; travel = tree->root; } } void TraverseQueue(pQueue *queue) { pQueueNode *iterator = queue->first; while(iterator != NULL) { cout<<iterator->val->symbol<<":"<<iterator->priority<<endl; iterator = iterator->next; } } void TraverseTable(HlTable *table) { HlNode *iterator = table->first; cout<<"字符"<<":"<<"編碼"<<endl; while(iterator != NULL) { cout<<iterator->symbol<<":"<<iterator->code<<endl; iterator = iterator->next; } cout<<endl; } int main() { HTree *codeTree = BuildTree("abbccccddddddd"); HlTable *codeTable = BuildTable(codeTree); TraverseTable(codeTable); cout<<"編碼開始:"<<endl; encode(codeTable,"abcd"); cout<<"\n解碼開始:"<<endl; decode(codeTree,"10100100000101"); cout<<endl; return 0; }
測試結果:
優先隊列變化過程:
生成的赫夫曼樹:
註:這裏的構建赫夫曼樹的約定和之前的兩篇不太一樣,這裏約定一個節點的左節點的權值小於右節點的權值.
使用優先隊列構建赫夫曼樹