您的位置:首页 > 理论基础 > 数据结构算法

数据结构全攻略--攻破非线性结构的堡垒之哈弗曼树篇

2013-10-14 09:38 351 查看
上篇着重讨论了二叉排序树的一些性质,它其实是一种排序结构,按照中序遍历得到的是一种按照顺序排列好的线性结构。接下来将会着重讨论哈弗曼树。

一、满二叉树和完全二叉树

什么是满二叉树呢?抛开它的概念不说,从它的字面意思上也可以看出满二叉树是一种丰满的二叉树,它要求除叶子结点外的结点都包括左孩子和右孩子。如下图:




那完全二叉树是什么结构呢?我们看看下图:




能从上图中看出满二叉树和完全二叉树的关系吗?




从上图中可以看出两种是包含的关系,完全二叉树中的特殊情况是一种满二叉树。对于完全二叉树和满二叉树的一些性质就不在细说,是一些很基础的东西,如果还不懂的可以去查书找到答案。

二、最优二叉树

为何引入最优二叉树,如何构建最优二叉树?这将是接下来讨论的重点。
首先,来看看最优二叉树的一些基本概念。

1、带权路径长度

结点=路径长度X结点的权值(结点所代表数值的大小);
树=所有结点带权路径长度的和。

最优二叉树就是一类带权路径长度最短的树,它又称为哈弗曼树。

2、那如何构造哈弗曼树呢?

(1)假设有一组二叉树结点的集合F,首先在F中选取两棵权值最小的树作为左右子树构造一棵新的二叉树,置新构造二叉树的根结点的权值为其左右子树根结点的权值之和。
(2)从F中删除这两棵树,同时将新得到的二叉树加入到F中。
(3)重复(1)(2)步,直到F中只含一棵树时为止,这棵树便是最优二叉树。



最优二叉树创建算法:

void createHTree(HuffmanTree HT,char *c,int *w,int n)
/*数组c[0...n-1]和w[0..n-1]存放了n个字符及其概率,构造哈弗曼树HT*/
{          int i, s1,s2;
	if(n<=1) return;
	
	for(int i;i<=n;i++){       /*根据n个权值构造n棵只有根结点的二叉树*/
		HT[i].ch=c[i-1];
		HT[i].weight=w[i-1];
		HT[i].parent=HT[i].lchild=HT[i].rchild=0;
	}/*for*/
	
	for(;i<2*n;++i){       /*初始化*/
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}/*for*/
	
	for(i=n+1;i<2*n;i++){
		/*从HT[1..i-1]中选择parent为0且weight最小的两棵树,其序号为s1和s2*/
		select(HT,i-1,s1,s2);
		HT[s1].parent=i;
		HT[s2].parent=i;
		HT[i].lchild=s1;
		HT[i].rchild=s2;
		HT[i].weight=HT[s1].weight+HT[s1].weight;
	}/*for*/
}/*createHTree*/



3、哈弗曼编码

哈弗曼编码是在哈弗曼树的基础上对哈弗曼树中的结点进行编码,把左子树的路径编码为0,右子树路径编码为1。




这种编码是为了提高通信效率,因为结点的访问频率不相同,如果采用等长编码的方法对通信原文进行编码,所得的电文的码串就会过长,不利于提高通信效率,因此希望缩短码串的长度。如果对每个字符设计长度不等的编码,且让电文中出现频率较高的字符采用尽可能短的编码,那么不但可以减少码串长度,同时也提高了通信效率,于是引入了哈弗曼编码。

最优二叉树进行译码算法:

void Decoding(HuffmanTree HT,int n,char *buff)
/*利用具有n个叶子结点的最优二叉树(存储在数组HT中)进行译码,叶子的下标为1~n*/
/*buff指向原文的编码序列*/
{          int p=2*n-1;
	While(*buff){
		if(*buff=='0')
			p=HT[p].lchild;     /*进入左分支*/
		else
			p=HT[p].rchild;    /*进入右分支*/
		
		if(HT[p].lchild==0 && HT[p].rchild==0){
			printf("%c",HT[p].ch);
			p=2*n-1;         /*回到树根*/
		}/*if*/
		
		buff++
	}/*while*/
}/*Decoding*/




那哈弗曼编码怎么求呢?
通过上图得到了基本的哈弗曼编码,确定了每个结点的最优前缀码,同时也能确定结点的哈弗曼编码,它是从叶子节点为出发点,向上回溯至根结点为止,上溯时走左分支则生成代码0,走右分支则生成代码1。

哈弗曼编码的构造方法:

void HuffmanCoding(HuffmanTree HT,HuffmanCode HC,int n)
/*n个叶子结点在哈弗曼树HT中的下标为1~n,第i(1≤i≤n)个叶子的编码存放HC[i]中*/
{
	char *cd;
	int I,start,c,f;
	
	if(n<=1)
		return;
	
	cd=(char *)malloc(n);
	cd[n-1]='\0';
	
	for(i=1;i<=n;i++){
		start=n-1;
		for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)
			if(HT[f].lchild==c)
				cd[--start]='0';
			else
				cd[--start]='1';
		
		HC[i]=(char*)malloc(n-start0;
		strcpy(HC[i],&cd[start]);
	}/*for*/
	
	delete cd;
}/*HuffmanCoding*/




三、结语

哈弗曼树的一些基本概念已经讨论完成,另外穿插了一些有关完全二叉树和满二叉树的一些概念,因为这两种二叉树型结构都是特殊的二叉树,满二叉树都知道它的构造方式,但是对于完全二叉树呢?完全二叉树有时候很难区分,所以拿出来重点讨论了下,是不是对非线性结构有个更加深刻的了解了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐