• 数据结构之树(Tree)(一)_树的基础


    该篇是关于树的概述,主要介绍什么是树、树的特点、树的表示方法、树的种类、树在存储结构中的表示、树/森林/二叉树之间的转换(原理)等,关于具体树(二叉树)的实现以及查找遍历等后续总结。

    树的概述及特点

    树是由n个(n>=0)结点组成的一个具有层次关系的集合。
    如图,是一个普通的树的图像表示:
    tree1
    从这个图像中大致能看出为什么把这种层次关系的集合称为树了,这个就像一个倒着的树,树跟在上,树叶在下,中间是树干。

    由 m(m >= 0) 个互不相交的树 组成的集合被称为森林。如上图,若以B、C为根节点的2棵子树就可以称为森林。

    树的结点

    树中的每个元素成为“结点”。

    特殊的结点
    • 根结点:没有父结点的结点。如上图的A。
    • 叶子结点:没有子结点的结点,如上图的D、E、F、C都是叶子结点。
    结点之间的特殊关系
    • 父结点(或称为双亲结点):一个结点含有子结点,那么该结点称为其子结点的父结点。如上图,A是B、C的父结点。
    • 子结点(或称为孩子结点):子结点和父结点是相对的。如上图,B、C就是A的子结点。
    • 兄弟结点:具有相同的父结点称为兄弟结点。如上图,B、C即是兄弟结点,D、E、F也是兄弟结点。

    树的特点

    • 每个结点有0个或多个子结点。
    • 非空树有且只有一个根结点。
    • 非根结点有且只有一个父结点。

    几个概念

    • 结点的度:一个结点含有的子结点数,即结点的度。如上图,A结点的度为2,B结点的度为3。
    • 树的度:一颗树中所有结点的度的最大值即树的度。如上图,树的度是3。
    • 结点的层次:从根结点开始,根结点为第一层,根结点的子结点为第二层,依次类推。如上图,A为第一层,B、C为第二层,D、E、F为第三层。
    • 树的高度(或树的深度):树中结点所在最大层次。如上图,树的高度即3。

    树的表示方法

    树的表示方法有很多,最常用的就是图像表示法。
    这里就介绍两种比较常见的,图像表示法和符号表示法。

    • 图像表示法

    如最开始的图像表示(如上图)。各结点之间的关系很清晰。

    • 符号表示法

    用括号先将根结点放入一对圆括号中,然后把它的子树由左至右的顺序放入括号中,而对子树也采用同样的方法处理,同层子树之间用逗号隔开。上图用符号表示法表示为:(A(B(D,E,F),C))

    树的种类

    有序树与无序树

    树中结点的子结点之间是否有顺序关系,即谁在左边、谁在右边是否有规定的。
    这种,如果有规定 即子结点之间存在顺序关系,称为有序树
    相反,如果不存在顺序关系,则称为无序树

    二叉树

    二叉树是非常重要的一种,后续很多都与之关联。

    先看个示例图:
    tree_different

    每个结点最多含有两个子树的树称为二叉树。 二叉树是有序树。
    即各个结点的度不超过2。(也即直接的子结点数最多为2,上面讲的子结点、父结点基本都是直接的关系,不包含子结点的子结点或父结点的父结点)

    二叉树的一些特性:
    1.第i层,结点总数最多为:2i-1
    2.若树的深度为k,则二叉树结点总数最多为:2k-1。
    3.叶子节点数n0 与 度为2的结点数n2 的关系:n0 =n2+1。

    两种特殊二叉树

    • 满二叉树

    除叶子结点外,其他结点的度都是2(即只有两个子结点)的树 称为满二叉树。

    满二叉树的一些特性:
    1.满二叉树第i层,结点总数为:2i-1
    2.若树的深度为k,则满二叉树结点总数为:2k-1,叶子结点树为:2k-1
    3.满二叉树叶子节点都在对地层,不存在度为1的结点。
    4.满二叉树,若有n个结点,则树的深度为:k=log2(n+1)。 2k-1=n

    • 完全二叉树

    假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树称之为完全二叉树。
    即:如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。

    完全二叉树的一些特性:
    1.完全二叉树若结点总数为n,则树的深度为:⌊log2n⌋+1。
    ⌊ ⌋为取整,如⌊log26⌋+1=3
    2.假如完全二叉树中结点编号从上到下,从左到右依次从1到n。则有:
    如果i>1时,父结点为⌊i/2⌋。(i=1即根结点,无父结点)。
    如果2i>n, 则i无左孩子(为叶子结点);否则i的左孩子是2i。
    如果2i+1>n, 则i无右孩子;否则i右孩子是2i+1.

    • 平衡二叉树(AVL树)

    当且仅当任何节点的两棵子树的高度差不大于1的二叉树

    树在存储结构中的表示

    树在存储结构中的表示,主要有3种:双亲表示法、孩子表示法、孩子兄弟表示法

    双亲表示法

    双亲表示法,采用数组的存储方式。
    每个结点包含两个部分:数据部分和指向父结点(双亲结点)的指针。
    数组元素定义:

    data parent
    结点信息 结点的双亲结点在数组中下标

    如下,表示数组中元素的定义。

    ArrNode[] treeArr = new ArrNode[TREE_SIZE];
    class ArrNode {
    	//数据部分
    	Object data;
    	//双亲结点位置
    	int parent;
    }
    

    根结点没有父结点,所以第二个部分存储为 -1。
    示例:一颗普通树 用双亲表示法,存储示意图
    tree_parent

    双亲表示法中,结点很容易找到父结点。但如果要找孩子结点,则需要遍历后才能找到。

    孩子表示法

    孩子表示法,采用数组+单链表的存储方式。
    数组部分存储了结点的值,以及指向第一个孩子结点的指针。单链表存储的是某个结点的所有孩子结点,从左到右。所以N个结点,即数组大小为N,有N个链表。
    如下,表示数组中元素定义,以及链表中元素定义。
    数组元素定义:

    data firstChildNode
    结点信息 指向第一个孩子结点指针

    链表中元素定义:

    child next
    孩子结点在数组中的下标 指向下个孩子结点的指针
    ArrNode[] treeArr = new ArrNode[TREE_SIZE];
    
    //链表中存储的孩子结点定义
    class ChildNode {
    	//孩子结点对应的在数组中存储的下标值
    	int child;
    	//下个孩子结点
    	ChildNode next;
    }
    
    //数组中存储元素定义
    class ArrNode {
    	//数据部分
    	Object data;
    	//指向第一个孩子结点指针(引用)
    	ChildNode firstChildNode;
    }
    

    如果是叶子结点(即没有孩子结点),链表为空。
    示例:一颗普通树 用孩子表示法,存储示意图
    tree_child

    孩子表示法中,结点很容易找到孩子结点。但如果要找双亲结点,则需要遍历后才能找到。

    提示:双亲结点和孩子结点都有明显的缺陷,但我们也可以将他们结合起来。即在孩子表示法基础上,数组元素定义中增加双亲位置的指针(即双亲表示法中的parent定义)。这种被称作“双亲孩子表示法”
    数组元素定义:

    data parent firstChildNode
    结点信息 结点的双亲结点在数组中下标 指向第一个孩子结点指针

    链表中元素定义:

    child next
    孩子结点在数组中的下标 指向下个孩子结点的指针

    孩子兄弟表示法

    孩子兄弟表示法,采用链表的存储方式。
    通过孩子结点和兄弟结点来表示。

    data firstChild firstBrother
    结点信息 第一个孩子结点的指针(左孩子) 第一个兄弟结点的指针(右兄弟)
    //结点定义
    class TreeNode {
    	Object data;
    	TreeNode firstChild;
    	TreeNode firstBrother;
    }
    

    示例:一颗普通树 用孩子兄弟表示法,存储示意图
    tree_child_brother

    树、森林与二叉树之间的转换

    上面的孩子兄弟表示法也能感受到,那就是普通树转换成了一个二叉树。

    网上有很多关于树、森林与二叉树之间的转换。这里简单总结下。
    ---若有不对,请指出。

    树、森林转换成二叉树

    结合上面孩子兄弟表示方法理解。

    其实树 或者 森林 转换成二叉树是一样的。 因为树就是一个森林,单树森林。

    转换规则简单是:
    森林中第一颗树的根结点 作为 二叉树的根结点。然后所有结点 依次 遵循左孩子有兄弟即可。

    下面简单示意说明下。
    树(单树森林)转换二叉树

    //单树                 二叉树
       A                     A
     / |                   /
    B  C  D    =======>    B    
    |    /|              / 
    E   F G H            E   C
                              
                               D
                              / 
                             F  
                              
                               G
                                
                                 H
    

    二叉树根结点即A(第一个棵树根结点),A的左孩子为B,右孩子为null(A是第一个树根结点,无兄弟)。然后看B,左孩子为E,右孩子为C(B、C、D兄弟结点)。再看E、C,依次类推即可将树转换成二叉树。

    森林转换二叉树
    同树转换成二叉树一样,将森林中的树看成兄弟即可。

    //        森林                        二叉树
                                           A	
                                         /   
       A       E     G                  /     			
     / | 	   |	/                 B       E
    B  C  D    F   H   I  =======>          / 
                   |                    C   F   G
                   J                          / 
                                          D   H
                                             / 
                                            J   I
    

    二叉树根结点即A(第一个棵树根结点),A的左孩子为B,右孩子为E(森林中的所有树看作兄弟结点)。然后看B,左孩子为null,右孩子为C(B、C、D兄弟结点),再看E,左孩子为F,右孩子为G。依次类推即可将森林转换成二叉树。

    二叉树转换成树、森林

    二叉树 转换成 树 或者 森林 也是一样的。 是上面树、森林转换成二叉树的逆向过程。

    转换规则简单是:
    二叉树的根结点即第一颗树的根结点。然后二叉树所有结点的 左孩子为(树或森林)结点的第一个孩子结点,右孩子为结点的兄弟结点。

    下面简单示意说明下。
    二叉树转换树(单树森林)

    //二叉树                   单树
        A
       /
      B    
     /                         A        
    E   C                     / |       
               =======>     B  C  D     
          D                  |    /|    
         /                   E   F G H   
        F                    			
        
         G
          
           H
    

    二叉树中A,有左孩子无右孩子。所以,该二叉树对应的是一棵树。A即树的根结点,左孩子B(二叉树中)即A的孩子结点(单树中)。看E、C(二叉树中),E为B左孩子,即E为B的孩子结点(孩子结点),C为B右孩子,即C为B的兄弟结点。依次类推即可。

    二叉树转换森林
    同理

    //  二叉树                           森林
           A                                         
         /                        A       E     G   
        /                       / |      |    / 	 
       B       E      =======>  B  C  D    F   H   I 
             /                               |	 
         C   F   G                             J	 
               /                                    
           D   H                                     
              /                                     
             J   I                                    
    

    二叉树中A,有左孩子有右孩子。所以,该二叉树对应的不是一颗树。A即第一颗树的根结点,左孩子B(二叉树中)即A的孩子结点(第一颗树中);右孩子E(二叉树中)是A的兄弟,A是第一颗树的根结点,所以E为第二颗树根结点。看B的左右孩子(二叉树中),左孩子为空即B(第一颗树中)没有孩子结点,右孩子为C即是B的兄弟结点 在第一颗树中。依次类推即可。

  • 相关阅读:
    移动端 line-height 不垂直居中问题
    CSS属性: 阴影 轮廓 渐变
    子div设置float后会导致父div无法自动撑开
    表格 滚动条 (tbody部分滚动)
    域控下发脚本,安装zabbix客户端
    cannot import name 'path' from django.urls
    添加私有yum库
    深夜感慨
    统计windows文件夹下文件大小(python脚本和powershell脚本)
    nginx日志切割
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/13149118.html
Copyright © 2020-2023  润新知