数据结构--哈夫曼树

Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树。

定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树。 这个定义里面涉及到了几个陌生的概念,下面就是一颗哈夫曼树,我们来看图解答。

(01) 路径和路径长度

定义:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。 
例子:100和80的路径长度是1,50和30的路径长度是2,20和10的路径长度是3。

(02) 结点的权及带权路径长度

定义:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。 
例子:节点20的路径长度是3,它的带权路径长度= 路径长度 * 权 = 3 * 20 = 60。

(03) 树的带权路径长度

定义:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。 
例子:示例中,树的WPL= 1*100 + 2*50 + 3*20 + 3*10 = 100 + 100 + 60 + 30 =290。

1 基本概念

① 结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径。

② 路径长度:结点路径上的分支数目称为路径长度。

③ 树的路径长度:从树根到每一个结点的路径长度之和。

④ 结点的带权路径长度:从该结点的到树的根结点之间的路径长度与结点的权(值)的乘积。

权(值):各种开销、代价、频度等的抽象称呼。

⑤ 树的带权路径长度:树中所有叶子结点的带权路径长度之和,记做:

        WPL=w1*l1+w2*l2+⋯+wn*ln=∑wi*li    (i=1,2,⋯,n)

其中:n为叶子结点的个数;wi为第i个结点的权值; li为第i个结点的路径长度。

⑥  Huffman树:具有n个叶子结点(每个结点的权值为wi) 的二叉树不止一棵,但在所有的这些二叉树中,必定存在一棵WPL值最小的树,称这棵树为Huffman树(或称最优树) 。

 

在许多判定问题时,利用Huffman树可以得到最佳判断算法。

   是权值分别为2、3、6、7,具有4个叶子结点的二叉树,它们的带权路径长度分别为:

(a)  WPL=2*2+3*2+6*2+7*2=36 ;

(b)   WPL=2*1+3*2+6*3+7*3=47 ;

(c)   WPL=7*1+6*2+2*3+3*3=34 。

其中(c)的 WPL值最小,可以证明是Huffman树。

2  Huffman树的构造

①  根据n个权值{w1, w2, ⋯,wn},构造成n棵二叉树的集合F={T1, T2, ⋯,Tn},其中每棵二叉树只有一个权值为wi的根结点,没有左、右子树;

②  在F中选取两棵根结点权值最小的树作为左、右子树构造一棵新的二叉树,且新的二叉树根结点权值为其左、右子树根结点的权值之和;

③  在F中删除这两棵树,同时将新得到的树加入F中;

④  重复②、③,直到F只含一颗树为止。

    构造Huffman树时,为了规范,规定F={T1,T2, ⋯,Tn}中权值小的二叉树作为新构造的二叉树的左子树,权值大的二叉树作为新构造的二叉树的右子树;在取值相等时,深度小的二叉树作为新构造的二叉树的左子树,深度大的二叉树作为新构造的二叉树的右子树。   

图6-25是权值集合W={8, 3, 4, 6, 5, 5}构造Huffman树的过程。所构造的Huffman树的WPL是:

WPL=6*2+3*3+4*3+8*2+5*3+5*3 =79

 

赫夫曼编码及其算法

1  Huffman编码

        在电报收发等数据通讯中,常需要将传送的文字转换成由二进制字符0、1组成的字符串来传输。为了使收发的速度提高,就要求电文编码要尽可能地短。此外,要设计长短不等的编码,还必须保证任意字符的编码都不是另一个字符编码的前缀,这种编码称为前缀编码。

        Huffman树可以用来构造编码长度不等且译码不产生二义性的编码。

    设电文中的字符集C={c1,c2, ⋯,ci, ⋯,cn},各个字符出现的次数或频度集W={w1,w2, ⋯,wi, ⋯,wn}。

Huffman编码方法

    以字符集C作为叶子结点,次数或频度集W作为结点的权值来构造 Huffman树。规定Huffman树中左分支代表“0”,右分支代表“1” 。

    从根结点到每个叶子结点所经历的路径分支上的“0”或“1”所组成的字符串,为该结点所对应的编码,称之为Huffman编码。

    由于每个字符都是叶子结点,不可能出现在根结点到其它字符结点的路径上,所以一个字符的Huffman编码不可能是另一个字符的Huffman编码的前缀。

若字符集C={a, b, c, d, e, f}所对应的权值集合为W={8, 3, 4, 6, 5, 5},如图6-25所示,则字符a,b, c,d, e,f所对应的Huffman编码分别是:10,010,011,00 ,110,111。

2  Huffman编码算法实现

(1)  数据结构设计

         Huffman树中没有度为1的结点棵有n个叶子结点的Huffman树共有2n-1个结点,则可存储在大小为2n-1的一维数组中。实现编码的结点结构如图6-26所示。

原因:

◆ 求编码需从叶子结点出发走一条从叶子到根的路径;

 

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct huffNode
{
	int weight;
	huffNode *L;
	huffNode *R;
};
bool comp(huffNode const *a, huffNode const *b)
{
	if ((*a).weight < (*a).weight)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int main()
{
	vector<huffNode *> map;
	vector<int> original;
	original.push_back(1);
	original.push_back(7);
	original.push_back(3);
	original.push_back(4);
	original.push_back(9);
	original.push_back(8);
	for (int i = 0; i<original.size(); i++)
	{
		huffNode *temp = new huffNode;
		(*temp).L = NULL;
		(*temp).R = NULL;
		(*temp).weight = original[i];
		map.push_back(temp);
	}
	sort(map.begin(), map.end(), comp);//若设计为非升序排序
	while (map.size() != 1)
	{
		huffNode *temp = new huffNode;
		(*temp).L = map[0];
		(*temp).R = map[1];
		(*temp).weight = (*map[0]).weight + (*map[1]).weight;
		map.erase(map.begin());
		map.erase(map.begin());
		map.push_back(temp);
		sort(map.begin(), map.end(), comp);
	}
	system("pause");

	return 0;
}

  

原文地址:https://www.cnblogs.com/277223178dudu/p/11430534.html