zoukankan      html  css  js  c++  java
  • 数据结构之树

    第一部分:定义

      树(Tree)是n(n>=0)个结点的有限集。 n = 0 时成为空树。在任意一颗非空树中: 

        (1). 有且仅有一个特定的成为根(root)的节点。

        (2). 当 n > 1 时,其余结点可以分为m个互不相交的有限集 T1、 T2、 T3 .....Tm,其中每一个集本身又是一棵树,并且称为根的子树

      

    第二部分:基本概念

      1. 结点分类

       在一棵树中,结点分为 根结点内部结点叶结点(又称终端结点)。

      结点拥有的子树的数目称为结点的度(Degree)。 B的度为1, D的度为3

      在一棵树中, 最大的结点的度为树的度。  由于D的度最多,所以树的度就是3.

      

      

       2.节点间关系

         其中B是A的孩子A是B的双亲, C是B的兄弟。 之所以称之为双亲是因为对于结点来说父母同体,只有这么称呼是比较合适的了。

      

        3. 其他相关概念

         结点的层次: 从根开始算起, 根为第一层,然后第二层,第三层....

          树的深度或高度: 结点的最大层次就是树的深度或高度。

          有序树:如果将树中结点的各子树看成是从左到右有次序的,不能互换的,那么该树就是有序树,否则就是无序树

    第三部分: 树的存储结构

       我们知道, 树中某个结点的孩子有多个,所以无论是使用顺序存储结构还是使用链式存储结构,都不能很好的表示树。但是我们可以利用顺序存储结构和链式犓结构的特点来实现之。

       一. 双亲表示法

       我们假设以一组连续的空间存储树的结构,同时在每个节点中,附加一个指示器指示其双亲结点在数组中的位置,也就是说,每个结点不仅仅知道自己是谁意外,还知道它的双亲在数组中的哪个位置。  

       即一个结点包含两个部分: 1.  data(数据域) --- 用于存储结点的数据信息。   2. parent (指针域) --- 用于存储该结点的双亲在数组中的下标

        

       注意: 这样的好处是我们可以根据结点的parent指针很容易的找出它的双亲结点,所以复杂度是O(1),  但是如果我们要知道结点的孩子是谁,就得遍历整个结构才行。为了解决这个问题,我们可以增加一个结点最左边孩子的域,可以称之为长子域。  孩子可以找到了, 但是找兄弟呢 ? 我们还可以增加一个兄弟域,这样,可以发现:

    存储结构的设计是一个非常灵活的过程,一个存储结构设计的是否合理,取决于该存储结构的运算是否合适、是否方便, 时间复杂度好不好等等。  

      

       二. 孩子表示法

        如果一棵树中的每一个结点都有很多的孩子,  那么这时孩子为重,  我们就可以考虑每个结点有多个指针域, 每个指针域指向一颗子树的根节点, 这种方法就是多重链表表示法。 不难想象,多重链表表示法的最终结构和一颗树的结构是无异的。但是根据一个结点需要多少个域的不同,我们可以将多重链表表示法分为下面两种:

       1. 每个结点的指针域的个数是数的度

         优点 这样做的好处在于树的结构是稳定的、整齐的、维护起来较为方便。 缺点 坏处在于有可能很多指针域都是空着的,造成了资源的浪费。

       2. 每个结点的指针域的个数就是其子节点的个数(即它的度)

          优点  这样做的好处在于不会造成资源的浪费。  缺点 坏处在于这样在后期维护起来会比较麻烦,且结构会比较混乱。

       ok!  那有没有一种方法可以回避上面的缺点呢? 当然有,这就是孩子表示法了, 孩子表示法,即以n个结点的单链表作为存储结构, 则n个结点就有n个孩子链表,每个结点又会引出孩子的节点(如果没有孩子,就不会引出,如果有,就一直引下去)。

        这样的表示法缺点在于,如果知道某个结点的双亲是谁呢?  并不难,只要我们把双亲表示法和孩子表示法综合一下不就行了吗?  这种思路当然可行

       三、 孩子兄弟表示法

      

    第四部分:二叉树

      二叉树(Binary Tree)是n个结点的有限集合,该集合或者为空集,或者由一个根节点和两颗互不相交的、分别称为根节点的左子树和右子树的二叉树组成。  

      1. 二叉树的特点

    1.  每个结点最多有两个子树。
    2.  左子树和右子树的顺组是不能随意颠倒的。
    3.  即使某个结点只有一个子树,也要分清楚左子树还是右子树。
    4. 三个结点可以构成 5 种不同的二叉树

      

      2. 一些特殊的二叉树

    1. 斜树  如上图中的第二个图和最后一个图就是斜树 ---  每一层都只有一个结点,结点的个数和二叉树的深度相同。
    2. 满二叉树 在一颗二叉树中,如果所有分支结点都存在左子树和右子树,并且所有的叶子都在同一层上, 这样的二叉树就称为满二叉树
    3. 完全二叉树  对于一颗具有n个结点的二叉树按层序编号,如果编号为i (1 <= i <= n)的结点与同样深度的满二叉树中编号为i的结点在树中的位置完全相同,则这棵树就是完全二叉树。  编号方式: 从上到下,从左到右。
    4. 注意: 满二叉树一定是完全二叉树, 完全二叉树不一定是满二叉树。

      

      3. 二叉树的5条性质

      性质一:  在二叉树的第i层上最多有2i-1  个结点。(i>=1).   如二叉树的第三层上最多有2 的 2 次方个结点。

      性质二: 在深度(或高度)为k的二叉树上最多有 2k - 1 个结点。  如深度为3的二叉树的结点数最多为 2 - 1 = 7个结点。

      性质三: 对于任意一颗二叉树T, 如果其终端的节点数为n ,度为2的节点数为n2, 则有n0= n+ 1; 

      性质四: 具有n个结点的完全二叉树深度为[log2n] + 1 (其中[x]表示不大于x的最大整数)。

      性质五: 

      如果对一颗有n个结点的完全二叉树(其深度为[log2n] + 1)的结点按层序编号(从上到下,从左到右), 对任一结点i (1 <= i <= n)都有:

        1. 如果 i = 1, 那么结点i是二叉树的根。 如果i > 1, 那么双亲是结点[i/2]。

        2. 如果 2i>n, 则结点 i 无左孩子,否则其做孩子是2i。

        3. 如果2i + 1 > n, 则结点i无右孩子, 否则其右孩子是结点 2i + 1;

      

    第五部分:遍历二叉树

    二叉树的遍历 ( traversing binary tree )是指从根节点出发, 按照某种次序依次访问二叉树中的所有结点,使得每个结点都被访问一次且仅被访问一次。  

      这里的两个关键词是 访问 和 次序。

      显然,二叉树的访问次序不同于线性结构,对于线性结构而言,最多也就是从头到尾、循环、双向等简单的遍历方式。树的结点之间不存在唯一的前驱和后继,所以在访问了一个结点之后,下一个被访问的结点面临着不同的选择。因而,对于二叉树而言,遍历结点的方式就完全不同了。

      说明:一般我们的习惯是从左到右的,这里同样也做出这样的限制。那么遍历时,前中后是指根节点被访问的时间上的前、中、后。 

      如前序遍历时先访问根节点、 中序历时中间的时候访问根节点、后续遍历是最后访问根节点。 不论是哪种遍历方式都是先左子树、后右子树。

    一: 前序遍历

      规则: 若二叉树为空, 则空操作返回,否则先访问根结点,然后前序遍历左子树,在前序遍历右子树。

       在二叉树中,先根后左再右。巧记:根左右。

    二: 中序遍历

      规则: 若树为空, 则空操作返回, 否则先从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。

         在二叉树中,先左后根再右。巧记:左根右。

      注意: 对称遍历就是中序遍历。

    三: 后续遍历

      规则: 若树为空,则空操作返回,否则从左到右先叶子结点的方式遍历访问左子树,最后是访问根节点。

      在二叉树中,先左后右再根。巧记:左右根。

    四:层序遍历

      规则: 若树为空,则空操作返回,否则从树的第一层开始,也就是根节点开始访问,从上而下逐层遍历,在同一层中,按照从左到右的顺序对结点逐个访问。

      这个是最为简单的,只要遵循从上到下、从左到右的规则即可。

    规则: 1. 这里的空操作返回,就是指向上返回,且空操作返回是共同点。   2. 这里的左子树、右子树和根节点都是相对的,环境和事件稍有改变,这些名词所代表的实体就发生了彻底的变化。

       3. 每次访问到一个节点,那么就要以这个节点作为根节点看作一个二叉树。

  • 相关阅读:
    自定义predicate来对List进行去重
    ParameterizedType
    万级TPS亿级流水-中台账户系统架构设计
    【Linux】【压测】基于python Locust库实现自动化压测实践
    家里宽带ADSL是城域网IP以及公网IP的具体区别和联系
    fiddler如何使用Xposed+JustTrustMe来突破SSL Pinning
    幽门螺旋杆菌检查方法那么多,到底选择哪个?
    apache (httpd)不支持中文路径问题
    皮肤瘙痒症、干燥瘙痒、荨麻疹和丘疹性荨麻疹区别和联系
    Redmi K30和Redmi K30 5G和Redmi K30 5G极速版和Redmi K30i 5G
  • 原文地址:https://www.cnblogs.com/zhuzhenwei918/p/6533381.html
Copyright © 2011-2022 走看看