哈夫曼编码
1 简介
1.1定义
哈夫曼树( Huffman )又称最优二叉树,是一类带权路径长度最短的树,有着广泛的应用。
1.2加权路径长度
在讨论哈夫曼树之前首先需要弄清楚关于路径和路径长度的概念。树中两个结点之间的路径由一个结点到另一结点的分支构成。两结点之间的路径长度是路径上分支的数目。树的路径长度是从根结点到每一个结点的路径长度之和。
设一棵二叉树有 n 个叶子结点,每个叶子结点拥有一个权值W1,W2,……Wn ,从根结点到每个叶子结点的路径长度分别为L1,L2,......Ln,那么树的加权路径长度为每个叶子的路径长度与该叶子权值乘积之和。通常记作。为了直观起见,在图中把带权的叶子结点画成方形,其他非叶子结点仍为圆形。请看图1中的三棵二叉树以及它们的带权路径长。
图1 具有不同带权路径长度的二叉树
注意:
这三棵二叉树叶子结点数相同,它们的权值也相同,但是它们的WPL带权路径长各不相同。图1中第三个树的WPL最小。它就是哈曼树,最优树。哈夫曼树是,在具有同一组权值的叶子结点的不同二叉树中,带权路径长度最短的树。也称最优树。
2. 哈夫曼树的构造
2.1 构造哈夫曼树的方法
对于已知的一组叶子的权值W1,W2,......,Wn。
①首先把n个叶子结点看做n棵树(仅有一个结点的二叉树),把它们看做一个森林。
②在森林中把权值最小和次小的两棵树合并成一棵树,该树根结点的权值是两棵子树权值之和。这时森林中还有n-1棵树。
③重复第②步直到森林中只有一棵为止。此树就是哈夫曼树。现给一组(n=4)具体的权值2,4,5,8,下边是构造具体过程:
图2 哈夫曼树构造过程
图2(a)是一个拥有4棵小树的森林,图2(b)森林中还有3子棵树,图2(c)森林中剩下2棵树,图2(d)森林只有一棵树,这棵树就是哈夫曼树。
2.2 相关问题
这里或许会提出疑问,n个叶子构成的哈夫曼树其带权路径长度唯一吗?确实唯一。树形唯一吗?不唯一。因为将森林中两棵权值最小和次小的子棵合并时,哪棵做左子树,哪棵做右子树并不严格限制。图2之中的做法是把权值较小的当做左子树,权值较大的当做右子树。如果反过来也可以,画出的树形有所不同,但WPL值相同。为了便于讨论交流在此提倡权值较小的做左子树,权值较大的做右子树。
3 实验
3.1 源码
#include <string>
#include <list>
using std::string;
using std::list;
typedef struct _HUFFMAN_TREE_NODE_
{
int nValue;
string strElement;
string strHuffmanCode;
_HUFFMAN_TREE_NODE_ *LChild,*RChild;
}Huffman,*PHuffman;
class Huffman_Code
{
public:
Huffman_Code(int n=0);
~Huffman_Code();
void Run();
void SetLeafNumber(int n) {nNodeNumber = n;}
private:
bool Init();//input data of every Huffman node
bool CreateHuffmanTree();//create a Huffman Tree
void HuffmanCoding(const Huffman* parent);
void Print(const Huffman* root);// print the result of coding
void DestroyNode(PHuffman root);
private:
list<Huffman> RawData;
int nNodeNumber;
PHuffman Root;
int nWPL;
};
sing std::cout;
using std::cin;
using std::endl;
Huffman_Code::Huffman_Code(int n):Root(NULL),nWPL(0)
{
nNodeNumber = n;
}
bool Huffman_Code::Init()
{
Huffman m_Node;
int i(0);
while (i<nNodeNumber)
{
cin>>m_Node.strElement;
cin>>m_Node.nValue;
m_Node.LChild = NULL;
m_Node.RChild = NULL;
RawData.push_back(m_Node);
++i;
}
return true;
}
bool Huffman_Code::CreateHuffmanTree()
{
PHuffman m_pTreeNodeL,m_pTreeNodeR,m_pTreeNodeParent;
while(RawData.size()>1)
{
m_pTreeNodeL = new Huffman;
*m_pTreeNodeL = RawData.front();
list<Huffman>::iterator iterErase=RawData.begin();
for (list<Huffman>::iterator iter = RawData.begin();iter != RawData.end();iter++)
{//find the min value
if (m_pTreeNodeL->nValue> iter->nValue)
{
m_pTreeNodeL->LChild = iter->LChild;
m_pTreeNodeL->RChild = iter->RChild;
m_pTreeNodeL->nValue = iter->nValue;
m_pTreeNodeL->strElement = iter->strElement;
m_pTreeNodeL->strHuffmanCode = iter->strHuffmanCode;
iterErase = iter;
}
}
RawData.erase(iterErase);
m_pTreeNodeR = new Huffman;
*m_pTreeNodeR = RawData.front();
iterErase = RawData.begin();
for (list<Huffman>::iterator iter = RawData.begin();iter != RawData.end();iter++)
{//find the min value
if (m_pTreeNodeR->nValue> iter->nValue)
{
m_pTreeNodeR->LChild = iter->LChild;
m_pTreeNodeR->RChild = iter->RChild;
m_pTreeNodeR->nValue = iter->nValue;
m_pTreeNodeR->strElement = iter->strElement;
m_pTreeNodeR->strHuffmanCode = iter->strHuffmanCode;
iterErase = iter;
}
}
RawData.erase(iterErase);
m_pTreeNodeParent = new Huffman;
m_pTreeNodeParent->LChild = m_pTreeNodeL;
m_pTreeNodeParent->RChild = m_pTreeNodeR;
m_pTreeNodeParent->nValue = m_pTreeNodeL->nValue+m_pTreeNodeR->nValue;
RawData.push_back(*m_pTreeNodeParent);
}
Root = new Huffman;
*Root = RawData.back();
RawData.clear();
return true;
}
void Huffman_Code::HuffmanCoding(const Huffman* parent)
{
if (parent)
{
if (parent->LChild)
{
parent->LChild->strHuffmanCode = parent->strHuffmanCode + "0";
HuffmanCoding(parent->LChild);
}
if(parent->RChild)
{
parent->RChild->strHuffmanCode = parent->strHuffmanCode+"1";
HuffmanCoding(parent->RChild);
}
}
}
void Huffman_Code::Print(const Huffman* root)
{
if (root)
{
if ( ! root->strElement.empty())
{
cout<<root->strElement<<" "<<root->strHuffmanCode<<" Value="<<root->nValue<<endl;
nWPL += root->strHuffmanCode.length()*root->nValue;
}
Print(root->LChild);
Print(root->RChild);
}
}
void Huffman_Code::Run()
{
bool bRet = Init();
if (bRet)
{
bRet = CreateHuffmanTree();
}
if (bRet)
{
HuffmanCoding(Root);
Print(Root);
cout<<"WPL= "<<nWPL<<endl;
}
}
void Huffman_Code::DestroyNode(PHuffman root)
{
list
Tree;
if (Root)
{
if(Root->LChild != NULL)
Tree.push_back(Root->LChild);
if(Root->RChild)
Tree.push_back(Root->RChild);
delete Root;
}
while(!Tree.empty())
{
PHuffman temp = Tree.front();
Tree.pop_front();
if(temp->LChild != NULL)
Tree.push_back(temp->LChild);
if(temp->RChild)
Tree.push_back(temp->RChild);
delete temp;
}
}
Huffman_Code::~Huffman_Code()
{
DestroyNode(Root);
}
void main()
{
Huffman_Code obj;
obj.SetLeafNumber(4);
obj.Run();
system("pause");
}
3.2 示例结果
输入:
a 2
b 4
c 5
d 8
输出:
d 0Value=8
c 10Value=5
a 110 Value=2
b 111 Value=4
WPL= 36