【哈夫曼树】哈夫曼树的实现以及哈弗曼编码

来源:互联网 发布:飞升真元锻体升级数据 编辑:程序博客网 时间:2024/05/24 01:58

基本概念

1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。

2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。

3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。

4、哈夫曼树

给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

构造哈夫曼树

Huffman树构造算法:

1、由给定的n个权值{w1,w2,w3,…,wn}构造n棵只有根节点的扩充二叉树森林F={T1,T2,T3,…,Tn},其中每棵扩充二叉树Ti只有一个带权值wi的根节点,左右孩子均为空。

2、重复以下步骤,直到F中只剩下一棵树为止:
a、在F中选取两棵根节点的权值最小的扩充二叉树,作为左右子树构造一棵新的二叉树。将新二叉树的根节点的权值为其左右子树上根节点的权值之和。
b、在F中删除这两棵二叉树;
c、把新的二叉树加入到F中;

这样最后得到哈夫曼树。

这里写图片描述

这里写图片描述

结论:从上图可以看出根节点的值为构建哈夫曼树所有节点的值和16 = 7+5+3+1

取两个值最小的值,可以用堆来实现。

#pragma once#include <iostream>#include <assert.h>#include <queue>#include <vector>template <typename T>struct HuffmanTreeNode{    HuffmanTreeNode(const T &data)    : _weight(data)    , _pLeft(NULL)    , _pRight(NULL)    , _pParent(NULL)    {}    T       _weight;    HuffmanTreeNode *_pLeft;    HuffmanTreeNode *_pRight;    HuffmanTreeNode *_pParent;};template <typename T>struct greater{    bool operator()(const T &left, const T &right)    {        return left->_weight > right->_weight;    }};template <typename T>class HuffmanTree{public:    HuffmanTree(const T *weight, int size, const T &invalid)        : pRoot(NULL)        , _invalid(invalid)    {        assert(NULL != weight && size >= 0);        _Create(weight, size);    }    ~HuffmanTree()    {        _Destroy(pRoot);    }    void LevelTraverse()    {        std::queue<HuffmanTreeNode<T> *> q;        if (NULL != pRoot)            q.push(pRoot);        while (!q.empty())        {            HuffmanTreeNode<T> *pCur = q.front();            q.pop();            std::cout << pCur->_weight << " ";            if (NULL != pCur->pLeft)                q.push(pCur->pLeft);            if (NULL != pCur->pRight)                q.push(pCur->pRight);        }        std::cout << std::endl;    }    HuffmanTreeNode<T> * GetRoot()    {        return pRoot;    }private:    void _Destroy(HuffmanTreeNode<T> * &pRoot)    {        if (NULL != pRoot)        {            _Destroy(pRoot->_pLeft);            _Destroy(pRoot->_pRight);            delete pRoot;            pRoot = NULL;        }    }    void _Create(const T *weight, int size)    {        if (0 == size)            return;        else if (1 == size)        {            if (*weight != _invalid)                pRoot = new HuffmanTreeNode<T>(*weight);        }        else        {            std::priority_queue<HuffmanTreeNode<T> *, std::vector<HuffmanTreeNode<T>* >, greater<HuffmanTreeNode<T>*> >                heap;            for (int i = 0; i < size; ++i)            {                if (weight[i] != _invalid)                {                    HuffmanTreeNode<T> *tmp = new HuffmanTreeNode<T>(weight[i]);                    heap.push(tmp);                }            }            HuffmanTreeNode<T> *pLeft, *pRight;            while (heap.size() >= 2)            {                pLeft = heap.top();                heap.pop();                pRight = heap.top();                heap.pop();                HuffmanTreeNode<T> *pParent = new HuffmanTreeNode<T>(pLeft->_weight + pRight->_weight);                pParent->_pLeft = pLeft;                pParent->_pRight = pRight;                pLeft->_pParent = pParent;                pRight->_pParent = pParent;                heap.push(pParent);            }            if (!heap.empty())                pRoot = heap.top();        }    }private:    HuffmanTreeNode<T>  *pRoot;    T                   _invalid; //非法值};

哈夫曼编码

统计字符出现的个数,然后进行构建哈夫曼树;
后序遍历哈夫曼树,左0右1,对每个叶子节点

这里写图片描述

注意:在建立不等长编码时,必须是任何一个字符的编码不能是另一个字符编码的前缀,这样才能保证译码的唯一性。

任何一个字符的huffman编码都不可能是另一个字符的huffman编码的前缀。

原创粉丝点击