1. 程式人生 > >使用優先隊列構建赫夫曼樹

使用優先隊列構建赫夫曼樹

tex encode b2c fontsize 結構 技術分享 abi enter row

關於赫夫曼編碼和赫夫曼樹的相關知識可參考之前兩篇文章(由二叉樹構造赫夫曼樹、赫夫曼編碼)。

本文介紹還有一種構建赫夫曼樹的方式,採用優先隊列.


步驟: 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;
}

測試結果:
技術分享

優先隊列變化過程:
技術分享
生成的赫夫曼樹:

技術分享
註:這裏的構建赫夫曼樹的約定和之前的兩篇不太一樣,這裏約定一個節點的左節點的權值小於右節點的權值.

使用優先隊列構建赫夫曼樹