哈夫曼编码

哈夫曼编码

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 哈夫曼编码是一种使用变长编码来减少数据传输量的有效方法。在 Verilog 中,我们可以通过以下步骤实现哈夫曼编码。 首先,我们需要构建哈夫曼编码树。这棵树是由输入数据的频率构建而成的。可以使用哈夫曼树构建算法,该算法会根据输入数据的频率构造出最优的哈夫曼编码树。 然后,我们需要根据构建好的哈夫曼编码树生成对应的哈夫曼编码表。哈夫曼编码表将字符与对应的二进制编码一一对应起来。可以使用深度优先搜索的方法遍历哈夫曼编码树,生成哈夫曼编码表。 接下来,我们可以编写 Verilog 代码来实现哈夫曼编码。首先,我们需要定义输入数据的接口,并接收输入数据。然后,根据输入的字符,查询哈夫曼编码表,将对应的二进制编码输出。 最后,我们需要将输出的二进制编码进行传输。为了正确传输数据,我们需要在输出数据前加入标志位,表示输出数据的开始位置。在接收端,需要识别这个标志位,并将后续的二进制编码解码为对应的字符。 总结来说,哈夫曼编码在 Verilog 中的实现需要构建哈夫曼编码树,生成哈夫曼编码表,并编写相应的编码和解码逻辑。这样可以实现对输入数据的高效压缩和解压缩。 ### 回答2: 哈夫曼编码是一种基于字符频率来构建编码的最优前缀编码方法。在Verilog中实现哈夫曼编码可以分为两步:构建哈夫曼树和生成编码表。 首先,构建哈夫曼树。我们可以使用二叉树的数据结构来表示哈夫曼树。在Verilog中,可以通过定义一个节点结构体来表示二叉树节点,其中包括字符和频率信息,以及左右子节点指针。通过比较字符频率来构建哈夫曼树,可以采用贪心算法,每次选择频率最小的两个节点合并为一个新节点,直到只剩下一个节点为止。 接下来,生成编码表。通过遍历哈夫曼树,可以得到每个字符的编码。在Verilog中,可以使用递归或者迭代的方式进行树的遍历。当遍历到叶子节点时,记录下路径上的0和1,即可得到每个字符的哈夫曼编码。可以使用一个数据结构来保存字符与编码的对应关系,比如使用一个二维数组或者哈希表。 最后,将哈夫曼编码应用于实际数据压缩或传输中。通过将原始数据按照对应的编码进行替换或者添加额外的标识,可以实现压缩和解压缩的功能。 总之,通过Verilog实现哈夫曼编码需要先构建哈夫曼树,然后生成编码表,最后将编码应用于数据压缩或传输中。这是一个相对复杂的任务,需要熟悉Verilog语言和数据结构的相关知识才能完成。 ### 回答3: 哈夫曼编码是一种变长编码的压缩算法,常用于将频率较高的字符用较短的编码表示,从而减小存储或传输的数据量。为了实现哈夫曼编码,可以使用硬件描述语言Verilog来设计相应的电路。 实现哈夫曼编码的Verilog电路需要以下几个主要模块: 1. 频率统计模块:用于统计输入文本中各个字符的频率。输入文本可以通过数据输入端口传入,使用计数器来统计每个字符出现的次数。 2. 构建哈夫曼树模块:根据字符频率构建哈夫曼树。使用二叉堆等数据结构来优化树的构建过程,按照频率大小进行排序。 3. 哈夫曼编码生成模块:根据构建好的哈夫曼树,生成每个字符对应的哈夫曼编码串。可以使用递归或者迭代的方式遍历哈夫曼树,同时记录每个字符的编码。 4. 编码器模块:将输入的文本按照生成的哈夫曼编码进行编码。通过读取输入文本的每个字符,并查找对应的哈夫曼编码,输出对应的编码串。 5. 译码器模块:将编码后的二进制串按照哈夫曼编码进行译码,得到原始文本。根据哈夫曼编码树进行译码,从根节点开始依次查找对应的字符。 以上模块可以通过组合逻辑电路来实现,可以利用状态机等技术进行控制。此外,还需要提供测试模块,用于验证设计的正确性。 总的来说,通过使用Verilog语言来设计实现哈夫曼编码的电路,可以实现对输入文本进行压缩和解压缩的功能。这种硬件实现可以提高编码运行速度,并减小对系统资源的占用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值