`

最优二叉树——哈夫曼树

 
阅读更多


一:什么是最优二叉树?

从我个人理解来说,最优二叉树就是从已给出的目标带权结点(单独的结点) 经过一种方式的组合形成一棵树.使树的权值最小. 最优二叉树是带权路径长度最短的二叉树。根据结点的个数,权值的不同,最优二叉树的形状也各不相同。它们的共同点是:带权值的结点都是叶子结点。权值越小的结点,其到根结点的路径越长

官方定义:

在权为wl,w2,…,wn的n个叶子所构成的所有二叉树中,带权路径长度最小(即代价最小)的二叉树称为最优二叉树哈夫曼树

二:下面先弄清几个几个概念:

1.路径长度

在树中从一个结点到另一个结点所经历的分支构成了这两个结点间的路径上的分支数称为它的路径长度

2.树的路径长度
 树的路径长度是从树根到树中每一结点的路径长度之和。在结点数目相同的二叉树中,完全二叉树的路径长度最短。

3.树的带权路径长度(Weighted Path Length of Tree,简记为WPL)
  结点的权
:在一些应用中,赋予树中结点的一个有某种意义的实数。
  结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积。
  树的带权路径长度(Weighted Path Length of Tree):定义为树中所有叶结点的带权路径长度之和,通常记为:

其中:
n表示叶子结点的数目
wi
li分别表示叶结点ki的权值和根到结点ki之间的路径长度。
树的带权路径长度亦称为树的代价。

三:用一个例子来理解一下以上概念

【例】给定4个叶子结点a,b,c和d,分别带权7,5,2和4。构造如下图所示的三棵二叉树(还有许多棵),它们的带权路径长度分别为:

(a)WPL=7*2+5*2+2*2+4*2=36
(b)WPL=7*3+5*3+2*1+4*2=46
(c)WPL=7*1+5*2+2*3+4*3=35

其中(c)树的WPL最小,可以验证,它就是哈夫曼树。

注意:
① 叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。
② 最优二叉树中,权越大的叶子离根越近。
③ 最优二叉树的形态不唯一,WPL最小

四.哈夫曼算法

对于给定的叶子数目及其权值构造最优二叉树的方法,由于这个算法是哈夫曼提出来的,故称其为哈夫曼算法。其基本思想是:
  (1)根据给定的n个权值wl,w2,…,wn构成n棵二叉树的森林F={T1,T2,…,Tn},其中每棵二叉树Ti中都只有一个权值为wi的根结点,其左右子树均空。
  (2)在森林F中选出两棵根结点权值最小的树(当这样的树不止两棵树时,可以从中任选两棵),将这两棵树合并成一棵新树,为了保证新树仍是二叉树,需 要增加一个新结点作为新树的根,并将所选的两棵树的根分别作为新根的左右孩子(谁左,谁右无关紧要),将这两个孩子的权值之和作为新树根的权值。
  (3)对新的森林F重复(2),直到森林F中只剩下一棵树为止。这棵树便是哈夫曼树。
注意:
① 初始森林中的n棵二叉树,每棵树有一个孤立的结点,它们既是根,又是叶子
② n个叶子的哈夫曼树要经过n-1次合并,产生n-1个新结点。最终求得的哈夫曼树中共有2n-1个结点。
③ 哈夫曼树是严格的二叉树,没有度数为1的分支结点。

五:最优二叉树算法具体实现思路

在构造哈夫曼树时,可以设置一个结构数组HuffNode保存哈夫曼树中各结点的信息,根据二叉树的性质可知,具有n个叶子结点的哈夫曼树共有2n-1个结点,所以数组HuffNode的大小设置为2n-1,数组元素的结构形式如下:

weight

lchild

rchild

parent

其中,weight域保存结点的权值,lchild和rchild域分别保存该结点的左、右孩子结点在数组HuffNode中的序号,从而建立起结 点之间的关系。为了判定一个结点是否已加入到要建立的哈夫曼树中,可通过parent域的值来确定。初始时parent的值为-1,当结点加入到树中时, 该结点parent的值为其双亲结点在数组HuffNode中的序号,就不会是-1了。构造哈夫曼树时,首先将由n个字符形成的n个叶结点存放到数组HuffNode的前n个分量中,然后根据前面介绍的哈夫曼方法的基本思想,不断将两个小子树合并为一个较大的子树,每次构成的新子树的根结点顺序放到HuffNode数组中的前n个分量的后面。

具体实现:

1)存储结构


#define n 100               //叶结点数目

#define m 2*n-1             //树中结点总数

typedef struct

{

       floatweight;             //权值,设权值均大于零

       intlchild,rchild,parent;  //左右孩子及双亲指针

} HTNode;

 typedef HTNode HuffmanTree[m]; //哈夫曼树是一维数组


(2)赫夫曼算法的数组法构造

void CreateHuffmanTree(HuffmanTree T)  

{  

    int i,p1,p2;                        //构造哈夫曼树,T[m-1]为其根结点  

    InitHuffmanTree(T);       //T初始化  

    InputWeight(T);               //输入叶子权值至T[0..n-1]的weight域  

    for(i=n;i<m;i++)  

    {    

         SelectMin(T,i-1,&p1,&p2);//共进行n-1次合并,新结点依次存于T[i]中  

                 //在T[0…i-1]中选择两个权最小的根结点,其序号分别为p1和p2  

        T[p1].parent=T[p2].parent=i;  

        T[i].1child=p1;              //最小权的根结点是新结点的左孩子  

        T[i].rchild=p2;               //次小权的根结点是新结点的右孩子  

        T[i].weight=T[p1].weight+T[p2].weight;  

    }//for  

}//CreateHuffman  


C语言实现全部代码:

#include "stdio.h"

#include "stdlib.h"

#define m 100

 

struct ptree              //定义二叉树结点类型

{

int w;                   //定义结点权值

struct ptree *lchild;     //定义左子结点指针

struct ptree *rchild;     //定义右子结点指针

};

 

struct pforest              //定义链表结点类型

{

struct pforest *link;

struct ptree *root;

};

 

int WPL=0;                  //初始化WTL为0

struct ptree *hafm();

void travel();

struct pforest *inforest(struct pforest*f,struct ptree *t);

 

void travel(struct ptree *head,int n)                    

{

//为验证harfm算法的正确性进行的遍历

struct ptree *p;

p=head;

if(p!=NULL)

 {

      if((p->lchild)==NULL && (p->rchild)==NULL) //如果是叶子结点

     {

             printf("%d ",p->w);

             printf("the hops of the node is: %d/n",n);

      WPL=WPL+n*(p->w);    //计算权值

        }//if

travel(p->lchild,n+1);

travel(p->rchild,n+1);

}//if

}//travel

 

struct ptree *hafm(int n, int w[m])

{

struct pforest *p1,*p2,*f;

struct ptree *ti,*t,*t1,*t2;

int i;

f=(pforest *)malloc(sizeof(pforest));

f->link=NULL;

for(i=1;i<=n;i++)          //产生n棵只有根结点的二叉树

  {

      ti=(ptree*)malloc(sizeof(ptree));//开辟新的结点空间

   ti->w=w[i];               //给结点赋权值

   ti->lchild=NULL;

   ti->rchild=NULL;

   f=inforest(f, ti);

      //按权值从小到大的顺序将结点从上到下地挂在一颗树上     

 }//for

while(((f->link)->link)!=NULL)//至少有二棵二叉树

  {

 p1=f->link;

 p2=p1->link;

 f->link=p2->link;           //取出前两棵树

 t1=p1->root;

 t2=p2->root;

 free(p1);                 //释放p1

 free(p2);                 //释放p2

 t=(ptree *)malloc(sizeof(ptree));//开辟新的结点空间

 t->w = (t1->w)+(t2->w);        //权相加

 t->lchild=t1;

 t->rchild=t2;             //产生新二叉树

 f=inforest(f,t);

 }//while

 p1=f->link;

 t=p1->root;

 free(f);

 return(t);                  //返回t

 }

 

pforest *inforest(struct pforest *f,structptree *t)

{

//按权值从小到大的顺序将结点从上到下地挂在一颗树上     

struct pforest *p, *q, *r;

struct ptree *ti;

r=(pforest *)malloc(sizeof(pforest)); //开辟新的结点空间

r->root=t;

q=f;

p=f->link;             

while (p!=NULL)            //寻找插入位置

 {

  ti=p->root;

  if(t->w > ti->w)         //如果t的权值大于ti的权值

     {

        q=p;

     p=p->link;             //p向后寻找

    }//if

  else

      p=NULL;                  //强迫退出循环

 }//while

r->link=q->link;

q->link=r;                 //r接在q的后面

return(f);                 //返回f

}

 

void InPut(int &n,int w[m])

{

printf("please input the sum ofnode/n"); //提示输入结点数

scanf("%d",&n);      //输入结点数

printf ("please input weight of everynode/n"); //提示输入每个结点的权值

for(int i=1;i<=n;i++)

scanf("%d",&w[i]);  //输入每个结点权值

}

 

int main( )

{

struct ptree *head;

int n,w[m];

InPut(n,w);

head=hafm(n,w);

travel(head,0);

printf("The length of the best path isWPL=%d", WPL);//输出最佳路径权值之和

return 1;

}

 


分享到:
评论

相关推荐

    最优二叉树——哈夫曼树.doc

    树结构的特点是:它的每一个结点都可以有不止一个直接后继,除根结点外的所有结点都有且只有一个直接前趋。

    基于哈夫曼编码的图像压缩技术研究

    摘 要:哈夫曼编码是一种数据编码方式,以哈夫曼树——即最优二叉树,用带权路径长度最小的二叉树,对数据进行重编码,经常应 用于数据压缩。在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又称“熵编码法...

    数据结构——树

    树的定义和基本术语:根、孩子、子孙、双亲、兄弟、堂兄、路径等 二叉树的定义及5个基本性质。...最优二叉树的定义、构建及哈夫曼编码的构造。 树的存储方式。 树或森林与二叉树之间的相互转化 树及森林的遍历方法

    第五章 树与二叉树

    1、哈夫曼树也称最优二叉树,在实际中有着广泛的应用。 叶子节点的权值 是对叶子结点赋予的一个有意义的数值量。 二叉树的带权路径长度 设二叉树具有n个带权值的叶子节点,从根节点到叶子节点的路径长度与相应的叶子...

    简单迷宫程序和树的实现程序

    八方向迷宫的实现 内附详细的程序说说明 还有树的相关程序 哈夫曼树(Huffman树)——设有n个权值{w1,w2,……wn},构造一棵有n个叶子结点的二叉树,每个叶子的权值为wi,则wpl最小的二叉树叫~,也称最优二叉树

    疯狂的java讲义源码-CodeAndDecodeByHuffman:数据结构课设——HuffMan编码和解码(Java)

    课题:《哈夫曼树编码解码》 一、实验内容 运用哈夫曼编码的相关知识对任意文本文件进行编码、解码。 二、需要用的数据结构以及实现思路 需要用到二叉树、HuffMan树、递归等数据结构 二叉树 在计算机科学中,二叉树是...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    *5.6 C++处理字符串的方法——字符串类与字符串变量 5.6.1 字符串变量的定义和引用 5.6.2 字符串变量的运算 5.6.3 字符串数组 5.6.4 字符串运算举例 习题 第6章 指针 6.1 指针的概念 6.2 变量与指针 6.2.1 定义...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    *5.6 C++处理字符串的方法——字符串类与字符串变量 5.6.1 字符串变量的定义和引用 5.6.2 字符串变量的运算 5.6.3 字符串数组 5.6.4 字符串运算举例 习题 第6章 指针 6.1 指针的概念 6.2 变量与指针 6.2.1 定义...

    c语言实例解析——数据结构篇(42-74)

    050 二叉搜索树操作 051 二项式系数递归 052 背包问题 053 顺序表插入和删除 054 链表操作(1) 055 链表操作(2) 056 单链表就地逆置 057 运动会分数统计 058 双链表 059 约瑟夫环 060 记录个人资料 ...

    数据结构与算法分析Java语言描述(第二版)

    树4.1 预备知识4.1.1 树的实现4.1.2 树的遍历及应用4.2 二叉树4.2.1 实现4.2.2 例子:表达式树4.3 查找树ADT——二叉查找树4.3.1 contains方法4.3.2 findMin方法和findMax方法4.3.3 insert方法4.3.4 remove方法...

    数据结构与算法分析-Java语言描述(第2版)_2_2

    表达式树 4.3 查找树adt——二叉查找树 4.3.1 contains方法 4.3.2 findmin方法和findmax方法 4.3.3 insert方法 4.3.4 remove方法 4.3.5 平均情况分析 4.4 avl树 4.4.1 单旋转 4.4.2 双旋转 4.5...

    数据结构与算法分析-Java语言描述(第2版)_1_2

    表达式树 4.3 查找树adt——二叉查找树 4.3.1 contains方法 4.3.2 findmin方法和findmax方法 4.3.3 insert方法 4.3.4 remove方法 4.3.5 平均情况分析 4.4 avl树 4.4.1 单旋转 4.4.2 双旋转 4.5...

    数据结构与算法分析_Java语言描述(第2版)]

    树4.1 预备知识4.1.1 树的实现4.1.2 树的遍历及应用4.2 二叉树4.2.1 实现4.2.2 例子:表达式树4.3 查找树ADT——二叉查找树4.3.1 contains方法4.3.2 findMin方法和findMax方法4.3.3 insert方法4.3.4 remove方法...

    数据结构与算法分析 Java语言描述第2版

    树4.1 预备知识4.1.1 树的实现4.1.2 树的遍历及应用4.2 二叉树4.2.1 实现4.2.2 例子:表达式树4.3 查找树ADT——二叉查找树4.3.1 contains方法4.3.2 findMin方法和findMax方法4.3.3 insert方法4.3.4 remove方法...

    数据结构与算法分析_Java语言描述(第2版)

    4.3 查找树ADT——二叉查找树 4.3.1 contains方法 4.3.2 findMin方法和findMax方法 4.3.3 insert方法 4.3.4 remove方法 4.3.5 平均情况分析 4.4 AVL树 4.4.1 单旋转 4.4.2 双旋转 4.5 伸展树 4.5.1 一个简单的想法...

Global site tag (gtag.js) - Google Analytics