哈夫曼编码

哈夫曼编码

一、简介

1.1定义

哈夫曼编码(Huffman Coding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫作Huffman编码(有时也称为霍夫曼编码)。

1.2应用

哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。 在计算机信息处理中, 哈弗曼编码在信息论中应用举例

 

“哈夫曼编码是一种一致性编码法(又称熵编码法”),用于数据的无损耗压缩。这一术语是指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。这种方法是由David.A.Huffman发展起来的。 例如,在英文中,e的出现概率很高,而z的出现概率则最低。当利用哈夫曼编码对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个位。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。若能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。

1.3加权路径长度

在讨论哈夫曼树之前首先需要弄清楚关于路径和路径长度的概念。树中两个结点之间的路径由一个结点到另一结点的分支构成。两结点之间的路径长度是路径上分支的数目。树的路径长度是从根结点到每一个结点的路径长度之和。

例:设一棵二叉树有 个叶子结点,每个叶子结点拥有一个权值W1,W2,……W,从根结点到每个叶子结点的路径长度分别为L1L2......Ln,那么树的加权路径长度为每个叶子的路径长度与该叶子权值乘积之和。通常记作。为了直观起见,在图中把带权的叶子结点画成方形,其他非叶子结点仍为圆形。请看图1中的三棵二叉树以及它们的带权路径长。 

注意:

这三棵二叉树叶子结点数相同,它们的权值也相同,但是它们的WPL带权路径长各不相同。图1中第三个树的WPL最小。它就是哈曼树,最优树。哈夫曼树是,在具有同一组权值的叶子结点的不同二叉树中,带权路径长度最短的树。也称最优树。

二、构造哈夫曼树 

2.1思路

构造哈夫曼树非常简单,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。这样,新节点就是两个被替换节点的父节点了。如此循环,直到队列中只剩一个节点(树根)。

2.2 伪代码

// parent node
pNode = &nodes[nParentNode++];
// pop first child
pNode->pLeft = PopNode(pNodes, nBackNode--, false);
// pop second child
pNode->pRight = PopNode(pNodes, nBackNode--, true);
// adjust parent of the two poped nodes
pNode->pLeft->pParent = pNode->pRight->pParent = pNode;
// adjust parent frequency
pNode->nFrequency = pNode->pLeft->nFrequency + pNode->pRight->nFrequency;

2.3 构造哈夫曼树详细过程

对于已知的一组叶子的权值W1,W2......,Wn

首先把n个叶子结点看做n棵树(仅有一个结点的二叉树),把它们看做一个森林。

在森林中把权值最小和次小的两棵树合并成一棵树,该树根结点的权值是两棵子树权值之和。这时森林中还有n-1棵树。

重复第步直到森林中只有一棵为止。此树就是哈夫曼树。现给一组(n=4)具体的权值2458,下边是构造具体过程:

 

 哈夫曼树构造过程

2(a)是一个拥有4棵小树的森林,图2(b)森林中还有3子棵树,图2(c)森林中剩下2棵树,图2(d)森林只有一棵树,这棵树就是哈夫曼树。

2.4 需注意的问题

① n个叶子构成的哈夫曼树其带权路径长度唯一

② 树形唯一。

因为将森林中两棵权值最小和次小的子棵合并时,哪棵做左子树,哪棵做右子树并不严格限制。图2之中的做法是把权值较小的当做左子树,权值较大的当做右子树。如果反过来也可以,画出的树形有所不同,但WPL值相同。为了便于讨论交流在此提倡权值较小的做左子树,权值较大的做右子树。

三、程序实现

新建一个Win32控制台应用程序,工程命名为 HuffmanCode。添加一个头文件,文件名为HuffmanCode.h ,添加如下代码:

#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;
};

然后在 源文件 HuffmanCode.cpp中添加下面的代码:

// HuffmanCode.cpp : 定义控制台应用程序的入口点。 
#include "stdafx.h"
#include "HuffmanCode.h"
#include <iostream>
using namespace std;
 
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<PHuffman> 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);
}
 
int _tmain(int argc, _TCHAR* argv[])
{
Huffman_Code obj;
obj.SetLeafNumber(4);
obj.Run();
system("pause");
return 0;
}

以上即为代码实现部分。

结果:

输入:

a 3

b 5

c 2

d 10

e 2

f 7

运行结果:

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

法哥2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值