热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

树二叉树哈夫曼树

1.树需要注意的两点:n(n0)表示结点的个数,m表示子树的个数(1)n0时,树的根节点是唯一的。

1.树

  需要注意的两点:n(n>=0)表示结点的个数,m表示子树的个数

  (1)n>0时,树的根节点是唯一的

  (2)m>0时,子树的个数没有限制

  结点的度和树的度

  (1)结点的度是指结点拥有的子树数

  (2)树的度是指树的各结点的度的最大值

  树的深度(Depth)

  树中结点的最大层次

      1 / \2 3/\ \4 5 6
  此树的深度是3
  
  

  树和图有什么区别?
  树其实就是不包含回路的连通无向图。
  

     

  上面这个例子中左边的是一棵树,而右边的是一个图。因为左边的没有回路,而右边的存在1->2->5->3->1这样的回路。

  (1)正是因为树有着“不包含回路”这个特点,所以树就被赋予了很多特性。

  (2)一棵树中的任意两个结点有且仅有唯一的一条路径连通

  (3)一棵树如果有n个结点,那么它一定恰好有n-1条边。

2.二叉树
  特点:
    (1)每个结点最多有两棵子树
    (2)左子树和右子树都是有顺序的,次序不能任意颠倒
    (3)即使树中某结点只有一颗子树,也要区分是左子树还是右子树

       1 \3

   3就是1的右子树

   二叉树的常见性质:

    性质1 在二叉树的第i层上至多有2i-1个结点(i>=1)

    性质2 深度为k的二叉树至多有2k-1个结点(k>=1)

    性质3 满二叉树,在一棵深度为k且有2k-1个结点。完全二叉树,若一棵深度为k的二叉树,其前k-1层是一个棵满二叉树,而最下面一层(即第k层)上的结点都集中在该层最左边的若干位置上。

    性质4 对于任何一棵二叉树T,如果其终端结点数(叶子结点数)为n0,度为2的结点数为n2,则n0=n2+1

    性质5 具有n个结点的完全二叉树的深度为[log2n]+1

 

     栗子1:
     具有300个结点的二叉树,其高度(深度)至少为9
    9层:至多:29-1=512-1=511
    8层:至多:28-1=256-1=255
  
     栗子2:
     已知一颗完全二叉树的第6层(设根是第1层)有8个叶结点,则该完全二叉树的结点个数最多是多少?
   
 第6层至多有26-1=32个,因为有8个叶结点,所以有24个子结点。又因为是完全二叉树,则第7层最多有24*2=48个叶结点。

    前6层至多26-1=63个,所以该完全二叉树的结点数最多是48+63=111

     栗子3:

   一个具有20个叶子节点的二叉树、它有()个度为2的节点 
    可知n0=20,由n0=n2+1,可以得到
n2=19

   栗子4:
   设树T的度为4,其中度为1,2,3和4的结点个数分别为4,2,1,1,则T中的叶子数为()

一棵含有n个结点的树,有n-1个分支,即 n = 1*4 + 2*2 + 3*1 + 4*1 + 1 = 16;

    又由于 n = n0 + n1 + n2 + n3 + n4 = n0 + 8;

    n0 + 8 = 16,所有叶子结点个数为8
     栗子5:
    
对于有n个结点的二叉树,其高度为()

    正确答案: D   

    A.nlog2n

   B.log2n

   C.[log2n]+1

   D.不确定

  解释:如果是完全二叉树则是[log 2 n]+1,有计算公式。其他的二叉树没有规律,是没有计算公式的,也是不确定的,只能知道其高度的范围是:[log2n ]+1 到 n

       栗子6:

   完全二叉树共有700结点,该二叉树有多少个叶子结点? 

 对于二叉树总的结点数是:n=n0+n1+n2 
  由性质4知,n0=n2+1 
  所以,n0+n1+n0-1=700,又n1只能去0或1,故此处选1
  2n0=700,n0
=350

 完全二叉树和满二叉树
    
  

   二叉树的两种存储结构

    (1)顺序存储(一般只用于完全二叉树)适用性不强

    对于完全二叉树而言,可以使用顺序存储结构。但是对于一般的二叉树来说,使用存储结构会有两个缺点:
    一、如果不是完全二叉树,则必须将其转化为完全二叉树,
    二、是增加了很多虚节点,浪费资源空间。
  

    (2) 链式存储

    这是最常用的一种二叉树存储结构。

    每个结点设置三个域,即值域,左指针域和右指针域,用data表示值域,lchild和rchild分别表示指向左右子树的指针域。如图所示。

    


  遍历二叉树
  前,中,后序遍历,这个前、中、后都是相对于根节点而言的,都是从根节点出发,按照某种次序一次访问二叉树中的所有结点,使得每个结点被访问的次数有且只有一次
  前序遍历(先根遍历):根-->左-->右
  中序遍历(中根遍历):左-->根-->右(左,是从最下层结点的左子树开始遍历)
  后序遍历(后根遍历):叶子-->结点-->根节点(按照先左子树,后右子树,最后访问根节点)
  层序遍历:从树的第一层,也就是根节点开始访问,从上到下一层一层遍历,其中在同一层,就按照从左到右的顺序访问
    
前序遍历:
12-9-76-35-22-16-48-46-40-90-
中根遍历:
9--12--16--22--35--40--46--48--76--90--
后根遍历:
9---16---22---40---46---48---35---90---76---12---

实现代码:二叉树的创建和遍历都是利用了递归的思想

package package2;
public class BinaryTree {int data; //根节点数据BinaryTree left; //左子树BinaryTree right; //右子树public BinaryTree(int data) //实例化二叉树类{this.data = data;left = null;right = null;}public void insert(BinaryTree root,int data){ //向二叉树中插入子节点if(data>root.data) //二叉树的左节点都比根节点小{if(root.right==null){root.right = new BinaryTree(data);}else{this.insert(root.right, data);//利用了递归}}else{ //二叉树的右节点都比根节点大if(root.left==null){root.left = new BinaryTree(data);}else{this.insert(root.left, data);//利用了递归}}}
}/*当建立好二叉树类后可以创建二叉树实例,并实现二叉树的先根遍历,中根遍历,后根遍历,代码如下:*/
package package2;
public class BinaryTreeTraverse {public static void preOrder(BinaryTree root){ //先根遍历if(root!=null){System.out.print(root.data+"-");preOrder(root.left);preOrder(root.right);}}public static void inOrder(BinaryTree root){ //中根遍历if(root!=null){inOrder(root.left);System.out.print(root.data+"--");inOrder(root.right);}}public static void postOrder(BinaryTree root){ //后根遍历if(root!=null){postOrder(root.left);postOrder(root.right);System.out.print(root.data+"---");}}public static void main(String[] str){int[] array = {12,76,35,22,16,48,90,46,9,40};BinaryTree root = new BinaryTree(array[0]); //创建二叉树for(int i=1;i}

3.推导遍历结果
三种情况:
(1)已知前序遍历和中序遍历,可以唯一确定一棵二叉树

 (2)已知后序遍历和中序遍历,可以唯一确定一棵二叉树

   (3)已知前序遍历和后序遍历,是不能确定一棵二叉树的

推导方法:
(1)先确定根节点。可以根据前序的第一个元素或后序的最后一个元素来确定
(2)确定第一个根节点的左子树和右子树。可以根据中序来确定

栗子1:
已知前序ABCDEF,中序CBAEDF,还原此二叉树,并推出中序遍历的结果
(1)首先确定根节点是A,根据前序的第一个元素。
(2)由中序可知,A的左边是CB,右边是EDF
      A
     /  \
    B    D
   /     /  \
  C     E    F
可推出后序:CBEFDA

栗子2:
已知中序ABCDEFG,后序BDCAFGE,还原此二叉树,并推出中序遍历的结果

  (1)首先确定根节点是E,根据后序的最后一个元素。
  (2)由中序可知,E的左边是ABCD,右边是FG

  初步判断:

         E 
        /  \
       ABCD  FG
  再次判断:
          E
         /  \
        A    G
         \    /
         C   F
        /  \
       B   D

可推出前序:EACBDGF

栗子3:某二叉树的先根遍历序列和后根遍历序列正好相反,则该二叉树具有的特征是(A)   

    A.高度等于其结点数

    B.任一结点无左孩子

    C.任一结点无右孩子

    D.空或只有一个结点

解释:

  可以是全部都是左孩子,也可以是全部都是右孩子,所以在一起就合称高度等于其结点数

        A          A

       /             \

      B               B

     /                  \

    C                    C

  /                      \

 D                        D

先根遍历是:A-B-C-D          先根遍历是:A-B-C-D

后根遍历是:D-C-B-A          后根遍历是:D-C-B-A

4.哈夫曼树
哈夫曼树是一种带权路径长度最短二叉树,也称为最优二叉树。
(1)什么叫带权路径长度?
从该结点到树根之间路径长度与结点上的权的乘积
下面用一幅图来说明。
  

  它们的带权路径长度分别为:

  图a: WPL=5*2+7*2+2*2+13*2=54

  图b: WPL=5*3+2*3+7*2+13*1=48

  可见,图b的带权路径长度较小,我们可以证明图b就是哈夫曼树(也称为最优二叉树)

   (2)如何构建哈夫曼树?

    一般可以按下面步骤构建:

    (1)将所有左,右子树都为空的作为根节点。

    (2)在森林中选出两棵根节点的权值最小的树作为一棵新树的左,右子树,且置新树的附加根节点的权值为其左,右子树上根节点的权值之和。注意,左子树的权值应小于右子树的权值

    (3)从森林中删除这两棵树,同时把新树加入到森林中。

    (4)重复2,3步骤,直到森林中只有一棵树为止,此树便是哈夫曼树。

    下面是构建哈夫曼树的图解过程:

      

  (3)哈夫曼编码

    利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。

    树中从根到每个叶子节点都有一条路径,对路径上的各分支约定指向左子树的分支表示”0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为各个叶子节点对应的字符编码,即是哈夫曼编码。

    就拿上图例子来说:

    A,B,C,D对应的哈夫曼编码分别为:111,10,110,0

    用图说明如下:

    
注意:若有编码00,则至少必须有编码01,否则只一个结点构成不了双亲,也就是说,不是二叉树了。
如:00,100,101,110,111不可能是哈夫曼编码
  
记住,设计电文总长最短的二进制前缀编码,就是以n个字符出现的频率作为权构造一棵哈夫曼树,由哈夫曼树求得的编码就是哈夫曼编码。
栗子1:
用二进制来编码字符串"abcdabaa",需要能够根据编码,解码回原来的字符串,最少需要多长的二进制字符串?
解析:哈夫曼编码问题。求二进制字符串长度其实就是求
带权最短路径长度。
   可以先构造哈夫曼树,字符串中,a有4个,b有2个,c有1个,d有1个,这些个数就是权值,先选最小的两个权值,c和d,

  如图:

  

所以字符串总长度:4*1+2*2+1*3+1*3=14

栗子2:

已知一段文本有1382个字符,使用了1382个字节进行存储,这段文本全部是由a、b、c、d、e这5个字符组成,a出现了354次,b出现了483次,c出现了227次,d出现了96次,e出现了232次,对这5个字符使用哈夫曼(Huffman)算法进行编码,则以下哪些说法正确()ACD

  A.使用哈夫曼算法编码后,用编码值来存储这段文本将花费最少的存储空间

  B.使用哈夫曼算法进行编码,a、b、c、d、e这5个字符对应的编码值是唯一确定的

  C.使用哈夫曼算法进行编码,a、b、c、d、e这5个字符对应的编码值可以有多套,但每个字符编码的位(bit)数是确定的

  D.b这个字符的哈夫曼编码值位数应该最短,d这个字符的哈夫曼编码值位数应该最长

解释:

A正确,Huffman树就是求最优解。可以有多套方案,但最终每套方案生成的编码长度都相同且都是最优解。
B错误,我们可以将左子树定为1右子树定为0也可以反之,不同的方案获得的编码值是不同的,但每个字符的编码长度是固定的
C正确,不同的方案影响的只是通向节点的路径为0还是1,而不会影响Huffman树的层次结构
D正确,生成了Huffman树之后,我们就能看到,出现频率越高的节点越靠近根,深度越小即编码值尾数越短;出现频率越低的节点越远离根,深度越大即编码位数越长

 栗子3:

一棵哈夫曼树共有215个结点,对其进行哈夫曼编码,共能得到(108)个不同的码字

解释:

  这个题目其实就是求有多少个叶子结点,就是度数为0的结点,因为哈夫曼树是二叉树,而且哈夫曼树中一定没有度数为1的结点。

  由n=n0+n2,和n0=n2+1,可以得到n2=107,所以n0=108

栗子4:给字母重新进行二进制编码,以使得"MT-TECH-TEAM"(包含连字符,不包含引号)的长度最小.并能够根据编码,解码回原来的字符串.请问最优编码情况下该字串的长度是多少bit?

解释:哈夫曼编码,统计每个单词出现的次数,进行排序,每次合并最小的两个,把合并的值带入,删除原来的两个值后,继续排序,直到最后只剩下一棵树

M:2  H:1

T:3  A:1

E:2  -:2

C:1

 

参考文档:
http://blog.sina.com.cn/s/blog_70600f720100ujnp.html
http://www.cnblogs.com/mcgrady/p/3329825.html

转:https://www.cnblogs.com/GumpYan/p/5748637.html



推荐阅读
  • 三角测量计算三维坐标的代码_双目三维重建——层次化重建思考
    双目三维重建——层次化重建思考FesianXu2020.7.22atANTFINANCIALintern前言本文是笔者阅读[1]第10章内容的笔记,本文从宏观的角度阐 ... [详细]
  • Cookie学习小结
    Cookie学习小结 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 短暂的人生中,IT和技术只是其中的一部分。无论换工作还是换行业,最终的目标是成功、荣誉和收获。本文探讨了技术人员如何跳出纯技术的局限,实现更大的职业发展。 ... [详细]
  • 本文详细解析了ASP.NET 2.0中的Callback机制,不仅介绍了基本的使用方法,还深入探讨了其背后的实现原理。通过对比Atlas框架,帮助读者更好地理解和应用这一机制。 ... [详细]
  • 机器学习算法:SVM(支持向量机)
    SVM算法(SupportVectorMachine,支持向量机)的核心思想有2点:1、如果数据线性可分,那么基于最大间隔的方式来确定超平面,以确保全局最优, ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • 本文介绍了如何在 Spring 3.0.5 中使用 JdbcTemplate 插入数据并获取 MySQL 表中的自增主键。 ... [详细]
  • 双指针法在链表问题中应用广泛,能够高效解决多种经典问题,如合并两个有序链表、合并多个有序链表、查找倒数第k个节点等。本文将详细介绍这些应用场景及其解决方案。 ... [详细]
  • 本文介绍了几种常用的图像相似度对比方法,包括直方图方法、图像模板匹配、PSNR峰值信噪比、SSIM结构相似性和感知哈希算法。每种方法都有其优缺点,适用于不同的应用场景。 ... [详细]
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • 题目解析给定 n 个人和 n 种书籍,每个人都有一个包含自己喜好的书籍列表。目标是计算出满足以下条件的分配方案数量:1. 每个人都必须获得他们喜欢的书籍;2. 每本书只能分配给一个人。通过使用深度优先搜索算法,可以系统地探索所有可能的分配组合,确保每个分配方案都符合上述条件。该方法能够有效地处理这类组合优化问题,找到所有可行的解。 ... [详细]
  • 深入解析Java虚拟机的内存分区与管理机制
    Java虚拟机的内存分区与管理机制复杂且精细。其中,某些内存区域在虚拟机启动时即创建并持续存在,而另一些则随用户线程的生命周期动态创建和销毁。例如,每个线程都拥有一个独立的程序计数器,确保线程切换后能够准确恢复到之前的执行位置。这种设计不仅提高了多线程环境下的执行效率,还增强了系统的稳定性和可靠性。 ... [详细]
author-avatar
手机用户2702934045
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有