哈夫曼树详解
来源:互联网 发布:均不是c语言关键字 编辑:程序博客网 时间:2024/06/05 11:52
二叉树中有一种特别的树——哈夫曼树(最优二叉树),其通过某种规则(权值)来构造出一哈夫曼二叉树,在这个二叉树中,只有叶子节点才是有效的数据节点(很重要),其他的非叶子节点是为了构造出哈夫曼而引入的!
哈夫曼编码是一个通过哈夫曼树进行的一种编码,一般情况下,以字符:‘0’与‘1’表示。编码的实现过程很简单,只要实现哈夫曼树,通过遍历哈夫曼树,规定向左子树遍历一个节点编码为“0”,向右遍历一个节点编码为“1”,结束条件就是遍历到叶子节点!因为上面说过:哈夫曼树叶子节点才是有效数据节点!
如下图就是一个哈夫曼编码例子程序效果图:
下面就来一步步实现这个过程:
前面已经说关键是构造一棵哈夫曼树,只要构造出了这棵哈夫曼树,编码就很简单,但哈夫曼树的构造是根据实际的情况来构造的,并不是随随便便就构造的!既然是二叉树那么我们首先就定义一个二叉树结构:
structtree
{
}*tre;
其中权值是我们最需要关心的,因为我们就是要通过权值来构造,但权值怎么规定呢?当然是根据实际情况来!其中叶子节点是为了标记是叶子节点,便于后期编码!
为了简单说明,第一个例子就直接定义多个哈夫曼树节点,然后通过这些节点来构造出最终的哈夫曼树!
treetr[9]={{'a',true,5},{'b',true,2},{'c',true,9},{'d',true,3},{'e',true,6}};
tr是一个哈夫曼数组,其中每个元素都是一个哈夫曼树,我们的任务就是将这些元素“整合”起来,使它们联系起来构成一个哈夫曼树。初始时,数组每个元素都是没有联系的,我们的任务就是把它们通过struct tree *zuo,*you;//左右孩子
我们先通过语言叙述的方法来构造一棵哈夫曼二叉树:
a权值5
b权值2
c权值9
d权值3
e权值6
首先,取权值最小的两个节点“整合”出一个新的节点,该节点的权值为最小两个节点权值之和。如下图:
然后,将这个新的节点与剩下元素进行权值比较,依旧取最小的两个权值节点构造 新的节点,反复这个过程,直到取完所有元素,本例的哈夫曼树如下图:
其中叶子节点(也就是2,3,5,6,9)是有效的数据节点!构造时节点的左右顺序并不影响哈
曼树的构造,但会导致出现不同的编码,当然编码只要不出现前缀码就是正确的编码。
实现算法:
实现算法有很多种,关键是要理解它构造的原理。
通过上面的例子,我们知道构造一个哈夫曼树,需要的节点数数有效数据节点的2*n-1,其中
n是有效数据的个数,如上面例子,有效数据个数有5个,但最终构造出的哈夫曼树有2*5-1=9
个节点,所以根据这个性质就可写出一种算法:
treetr[9]={{'a',true,5},{'b',true,2},{'c',true,9},{'d',true,3},{'e',true,6}};
5个数据所以需要9个空间,其中9-5=4个空间是给那些无效节点使用的(哈夫曼树种非叶
子节点)。
首先,我们遍历这个数组,找到最小的两个元素。
然后,将他们移动到前面,并将权值求和构造出新的节点,新的节点左右子树指向最小的两个元素,将这个新节点插入有效数据后面。
最后,从第2+1个元素(前面两个无需遍历了)开始重新遍历。
重复上述过程,直到数组填满,填满后的最后一个元素就是最终的哈夫曼树。
如第一次遍历后数组tr[9]状态就变为:
treetr[9]={ {'d',true,3},{'b',true,2},{'c',true,9},{'a',true,5},{'e',true,6},{‘’,false,5,tr[0],tr[1]}}
最小的两个元素移到了前面,有效数据增加了一个,并且新节点左右子树指向前面两个元素。
完整哈夫曼实现代码如下;
// hfm.cpp: 定义控制台应用程序的入口点。
//
#include"stdafx.h"
#include
////////////贵州民族大学///////////////
///////////编译环境vs2010//////////////
static inthfmb=0;
structtree
{
}*tre;
structshfm
{
}hfm[100];//哈夫曼编码对应真实数据表
voidgettree(tree tr[],int shij,int youx)//构造哈夫曼树,tr树集合,shij集合实际数据个数,youx集合有效数据个数
{
}
voidbianltree(tree *tr,char ch[])//哈夫曼编码
{
}
int_tmain(int argc, _TCHAR* argv[])
{
//
}
效果:
其中gettree()函数是构造哈夫曼过程,bianltree()是通过哈夫曼树编码过程,struct shfm
结构体是保存字符数据与它的哈夫曼编码的映射表,可用也可不用,这里之所以使用,是因为通过一次遍历就可得到所有元素的编码,以后要编码只需查表即可,以空间换时间。
下面介绍哈夫曼的应用举例:
通过上文的介绍,下面就介绍哈夫曼的实际运用。
本例的模拟效果是:通过传入一串字符串,返回该字符串的编码。并且通过传入一个有效的编码得到一个字符串!
下面给出完整代码,该代码基于上述代码之上进行修改,并优化上述代码。
// hfm.cpp: 定义控制台应用程序的入口点。
//
#include"stdafx.h"
#include
////////////贵州民族大学///////////////
///////////编译环境vs2010//////////////
static inthfmb=0;
structtree
{
}*tre;
structshfm
{
}hfm[100];//哈夫曼编码对应真实数据表
voidgettree(tree tr[],int shij,int youx)//构造哈夫曼树,tr树集合,shij集合实际数据个数,youx集合有效数据个数
{
}
voidbianltree(tree *tr)//哈夫曼编码
{
}
voidchushihuahmf(tree *hfmtree,char *str,int i)
{//初始化哈夫曼树数组!参数:含哈夫曼树数组,待编码数据串,数据串长度
//注:本函数权值是根据字符ascll码判断!可根据实际情况重新定义初始化函数!
}
char *hfmbm(char *str)
{//哈夫曼编码函数
}
voidhfmjm(char *hmf)
{//哈夫曼解码参数哈夫曼编码后的数据串
}
int_tmain(int argc, _TCHAR* argv[])
{
}
测试效果1:
测试效果2:
函数说明:
char *hfmbm(char *str)函数是完成哈夫曼树构造的函数,用户只需传入一个带编码的字符串就可,本函数就可根据字符串开辟数组空间,并构造哈夫曼树。
voidchushihuahmf(tree *hfmtree,char *str,int i)函数是初始化哈夫曼树权值的函数,因为我们构造时需要指定构造规则(即权值),本函数为了方便,直接使用ascll码作为权值构造,本函数可根据实际情况修改。
voidhfmjm(char *hmf)函数是解码函数,通过传入有效编码解码出字符串!
从效果图中,我们发现相同元素有不同的编码,不过这不影响编码与解码,但从空间、时间角度应该避免这种情况,篇幅有效,本文将不再处理,在本例中由于有映射表,所有可以通过遍历映射表删除重复元素。
- 哈夫曼树详解
- 哈夫曼树详解
- 哈夫曼树详解
- 哈夫曼树详解
- 哈夫曼树详解
- 哈夫曼树详解<续>
- 哈夫曼树 之 Java详解
- 详解
- 详解
- 详解
- 详解
- &,&&,|,||详解
- 详解
- 哈夫曼树详解和C++实现
- 哈夫曼树(一)之 C语言详解
- Scala详解--------基础知识详解
- Spring详解-----------事务详解
- github 详解详解
- 【玩转SQLite系列】(一)初识SQLite,重拾sql语句
- java面试题总结(1)
- dubbo框架搭建
- Fragment与Fragment、Activity通信的四种方式(三)
- 第14周项目1-(2)验证分块查找算法
- 哈夫曼树详解
- uC/OS-ii移植详解
- Android Jni调用浅述
- 用matplotlib来画图-python学习笔记16
- 【NOIP 2016】 组合数问题 解题报告
- (2)C语言常用图形函数
- HDU2109 Fighting for HDU
- 学习 UML 核心元素
- Tomcat-----Linux下实时查看Tomcat日志