快牵着我的袜子

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、二叉搜索树特点

  1.1、简介

    一棵二叉树是结点的一个有限集合,该集合或者为空,或者由一个根结点加上两棵左子树和右子树组成,二叉树的特点就是左子树的结点值小,

  而右子树的结点值比父结点大。可以采取类似于二分查找的思想,正常情况下快速的找到这个结点。

  1.2、重点操作

    对于树中的每个结点X,它的左子树中所有关键字的值小于X的关键字值,而它的右子树中所有关键字值都大于X的关键字值

  根据这个特性,对一个二叉树进行中序遍历,如果时单调递增的,则可以说明这个数二叉搜索树重点操作有插入、删除

    1.2.1、插入:

      1、从根结点开始,遇键值较大则向左走,与键值较小则向右走,直至尾部,即插入点

    1.2.2、删除:

      1、如果存在于叶子结点,直接删除即可

      2、删除的结点只有一个子结点,则将子结点连至父结点的即可(可以视为将子结点替换掉删除结点的位置)

      3、删除的结点有两个子结点,这时需将右子树的最小值替换掉删除结点即可,最小结点可通过删除结点右子树一直向左走到底获得,

  二叉树:

    树中每个结点最多有两个子树。不排序。

  完全二叉树:

    对于深度为K,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一 一对应时称之为完全二叉树。

  满二叉树: 

    除最后一层无任何子结点外,每一层上的所有结点都有两个子结点的二叉树。

    一个二叉树,如果每一个层的结点数都达到最大值,则这个树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数为2^k-1,则它

  是满二叉树。

二、平衡二叉搜索树

  2.1、简介

    平衡二叉树,又称AVL树指的是左子树上的所有结点的值都比根结点的值小,而右子树上的所有结点的值都比根结点的值大,且任意一个结点左

  子树与右子树的高度差最大为1。

  2.2、重点操作

    发生的情况:当对二叉树进行插入或者删除的时候,有可能造成二叉搜索树失去平衡,造成搜寻效率低落的情况。

    2.2.1、插入:

      当向二叉搜索树插入一个结点分为ie四种情况

      1、插入结点位于X的左子结点的左子树--左左

      2、插入结点位于X的左子结点的右子树--左右

      3、插入结点位于X的右子结点的左子树--右左

      4、插入结点位于X的右子结点的右子树--右右

      情况1、4彼此对称,称为外侧插入,采用单旋转操作即可调整

      情况2、3彼此对成,称为内侧插入,可以采用双旋转(执行两次单旋转)操作调整。双旋转如下:

      插入结点的祖父节点旋转一次,新的祖父节点的父节点再旋转一次。

     

    2.2.2 删除 

      1、被删除结点没有儿子,即为叶结点。

        1、将该结点直接从树中删除;

        2、其父结点的子树高度的变化将导致父结点平衡因子的变化,通过向上检索并推算其父结点是否失衡;

        3、如果其父结点未失衡,则继续向上检索推算其父结点的父结点是否失衡…如此反复②的判断,直到根结点;如果向上推算过程中发

        现了失衡的现象,则进行④的处理;

        4、如果其父结点失衡,则判断是哪种失衡类型[LL、LR、RR、RL],并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与

        原来以父结点为根结点的树的高度发生变化,则继续进行②的检索推算;如果与原来以父结点为根结点的高度一致时,则可说明父结点

        的父结点及祖先结点的平衡因子将不会有变化,因此可以退出处理。

              

 

 

         我们将上图中节点8删除,得到下图:

              

 

 

       在左子树上删除节点其实就相当于在右子树上插入节点。但要如何调整还取决于该失衡节点的右孩子的左右子树的高度差。我们找到节点

    20的父节点的右子树上30,发现节点30的左右子树高度一样,因此我们只需进行RR型调整,也就是左旋一次,根据父节点的另一个子节点30进

    行逆时针旋转。调整后的树如下:

      

 

 

 

      2、被删除结点只有一个儿子。

        1、那么,直接删除该结点,并用该结点的唯一子结点顶替它的位置。

        2、以删除结点的父结点为推算点,依此向上检索推算各结点(父、祖先)是否失衡;

        3、如果其父结点未失衡,则继续向上检索推算其父结点的父结点是否失衡,如果有失衡则进行步骤4

        4、判断是哪种失衡类型[LL、LR、RR、RL],并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与原来以父结点为根结点

        的树的高度发生变化,则继续进行②的检索推算;如果与原来以父结点为根结点的高度一致时,则可说明父结点的父结点及祖先结点的

        平衡因子将不会有变化,因此可以退出处理。

        这可以用上面的例子,只是删除的节点变成了20。

      3、 被删除结点有两个儿子。 

        1、① 如果该结点的平衡因子为0或者1,则找到其左子树中具有最大值的结点max(我们只讨论有序平衡二叉树,并且有序平衡二叉树

        中任意一个结点的左子树上的所有结点的值小于该结点的值,右子树上所有结点的值大于该结点的值),将max的内容与x的内容交换

        (只替换保存的真正的数据,不替换指针,平衡因子等用于管理目的的信息),并且max即为新的要删除的结点。由于树是有序的,因

        而这样找到的结点要么是一个叶子结点,要么是一个没有右子树的结点。

        2、如果该结点的平衡因子为-1,则找到其右结点中具有最小值的结点min,将min的内容与x的内容交换,并且min即为新的要删除的节

        点。由于树是有序的,因而这样找到的结点要么是一个叶子结点,要么是一个没有左子树的结点

        通过上面的操作后,我们需要对值替换后的结点进行删除,根据树的性质,我们可以得到我们要输出的结点只有两种情况,叶子结点或

      者是只有一棵子树的结点。删除后的调整操作按照我们前面已经讲的两种方法去调整。

   2.3、输入一棵二叉树的根结点,求树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树深度。

    

1、利用递归的思想,对于每个节点,判断它的左子树深度为多少,判断它的右子树深度为多少
2、将左子树深度与右子树深度中最大的深度+1,即为该节点的深度
/*
实现:求二叉树深度的函数TreeDepth */ int TreeDepth(TreeNode* pRoot) { if(pRoot==NULL){ return 0; } int leftLen = TreeDepth(pRoot->left); int rightLen= TreeDepth(pRoot->right); return leftLen>rightLen ? (leftLen+1) : (rightLen+1); }

 

判断是否是平衡二叉树
/*
实现:判断是否是平衡二叉树
*/
//简单版,但是存在不足,就是会遍历很多重复的节点
bool IsBalanced(BinaryTreeNode* pRoot){
    if (pRoot == NULL){
        return true;
    }
    int leftLen = TreeDepth(pRoot->left);
    int rightLen = TreeDepth(pRoot->right);
    int diff = leftLen - rightLen;
    if (diff > 1 || diff < -1){
        return false;
    }
    return IsBalanced(pRoot->left) && IsBalanced(pRoot->right);
}

//利用后序遍历,这样对每个节点只需要遍历一次
bool IsBalanced(BinaryTreeNode* pRoot,int* depth){
    if (pRoot == NULL){
        *depth = 0;
        return true;
    }
    if (IsBalanced(pRoot->left, &leftLen) && IsBalanced(pRoot->right, &rightLen)){
        int diff = leftLen - rightLen;
        if (diff <= 1 || diff >= -1){
            *depth = leftLen > rightLen ? leftLen + 1 : rightLen + 1;
            return true;
        }
    }
    return false;
}

 

三、红黑树

  3.1、简介

    红黑树是一种自平衡二叉查找树,红黑树放弃追求完全平衡,追求大致平衡,与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多

  只需要三次选择就能达到平衡,实现起来也更为简单。平衡二叉树追求完全平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转

  的次数不能预知。

    红黑树不仅是一个二叉搜索树,还必须满足以下规则:

      1、每个结点不是红色就是黑色

      2、根结点一定为黑色

      3、每个叶子结点都是黑色的空结点(NIL结点)。//这里叶子结点,是指为空的叶子结点!

      4、如果结点为红色,则子结点必须为黑色    //红之子、之父比黑  插入、删除会影响这个规则

      5、任一结点至NULL(树尾端)的任何路径,所含之黑结点树必须相同。//外部结点到根:途中经过黑结点数目相同

       红黑树是主要通过性质五来约束树的高度,提高树的平衡性。因为从该结点到其子孙结点的所有路径上包含相同数目的黑结点,所以新插入

    的结点需要是红结点来保证黑结点数目相等。又通过性质四来维护树的平衡性。

  

  3.2、插入情况及解决方案:

    插入步骤:

    第一步: 将红黑树当作一颗二叉查找树,将结点插入。

    第二步:将插入的结点着色为"红色",插入为红色,不会违背,不会违背"特性(5)"!少违背一条特性。

    第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。

    核心思想:将红色的结点移到根结点;然后,将根结点设为黑色,先解决新插入结点引发的颜色问题,再解决到叶子结点经过黑结点数量问题。

    1、空树

     方案:检测是否是根结点,如果是,将颜色改为黑

    2、父亲结点p是黑色

      方案:不会破坏红黑树的性质,不需要改变

    3、父结点P红色,祖父结点G黑色,叔结点U红色

      方案:祖父结点必然是黑色,已经不满足红黑树的性质了。

      将当前结点的父结点和叔叔结点涂黑,祖父结点涂红,把当前结点指向祖父结点(递归平衡,进入新的情况)

      1、父亲涂黑:解决孩子是红的问题,产生包含‘父结点’的分支的黑色结点的总数增加了1的问题,

      2、祖父涂红:为了解决上述问题,所以将祖父涂红。但产生了新的问题:“包含‘叔叔结点’的分支的黑色结点的总数较少1”。

      3、叔叔涂黑:能解决““包含‘叔叔结点’的分支的黑色结点的总数减少1的问题, 同时着 “包含‘祖父结点’的分支的黑色结点的总数增加了1。

    4、父结点P红色,祖父结点为黑色,叔结点为黑色,且当前结点是其父结点的右孩子

      方案:以新当前结点的父结点左旋,当前结点的父结点做为新的当前结点(递归平衡,进入新的情况)

      1、左旋:要不断的将破坏红黑树特性的红色结点上移(即向根方向移动),若旋转后的结点变成了根结点,那么直接将其设为“黑色”,就完全

      解决问题了

      2、父结点做为新的当前结点:如选择后该结点不是根结点,则将父结点做为新的当前结点,因为他已经从父结点变为了子结点,从而破坏

      红黑树的性质。处理问题的时候,需要从下至上(由叶到根)方向进行处理。

    5、父结点P红色,祖父结点为黑色,叔结点为黑色,且当前结点是其父结点的左孩子

      方案:父结点变为黑色,祖父结点变为红色,在祖父结点为支点右旋。

      1、父结点变黑:解决孩子是红的问题,产生包含‘父结点’的分支的黑色结点的总数增加了1的问题。

      2、祖父结点变为红色并旋转:解决包含‘父结点’的分支的黑色结点的总数增加了1的问题。

  3.3、删除情况及解决方案

    

    在删除节点后,原红黑树的性质可能被改变,如果删除的是红色节点,那么原红黑树的性质依旧保持,此时不用做修正操作,如果删除的节点是

  黑色节点,原红黑树的性质可能会被改变,我们要对其做修正操作。那么哪些树的性质会发生变化呢,如果删除节点不是树唯一节点,那么删除节点

  的那一个支的到各叶节点的黑色节点数会发生变化,此时性质5被破坏。如果被删节点的唯一非空子节点是红色,而被删节点的父节点也是红色,那

  么性质4被破坏。如果被删节点是根节点,而它的唯一非空子节点是红色,则删除后新根节点将变成红色,违背性质2。”

 

    第一步:将红黑树当作一颗二叉查找树,将结点删除。

      1、被删除结点没有儿子,即为叶结点。那么,直接将该结点删除就OK了。

      2、被删除结点只有一个儿子。那么,直接删除该结点,并用该结点的唯一子结点顶替它的位置。

      3、 被删除结点有两个儿子。那么,先找出它的后继结点;然后把“它的后继结点的内容”复制给“该结点的内容”;之后,删除“它的后继节

      点”。在这里,后继结点相当于替身,在将后继结点的内容复制给"被删除结点"之后,再将后继结点删除。这样就巧妙的将问题转换为"删除

      后继结点"的情况了,下面就考虑后继结点。 在"被删除结点"有两个非空子结点的情况下,它的后继结点不可能是双子非空。既然"后继节

      点"不可能双子都非空,就意味着"该结点的后继结点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个

      儿子,则按"情况② "进行处理。

    第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。

      因为"第一步"中删除结点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。

       

 

    修复的情况有点复杂:下面我们用一个分析技巧:我们从被删结点后来顶替它的那个结点开始调整,并认为它有额外的一重黑色。这里额外一重

  黑色是什么意思呢,我们不是把红黑树的结点加上除红与黑的另一种颜色,这里只是一种假设,我们认为我们当前指向它,因此空有额外一种黑色,

  可以认为它的黑色是从它的父结点被删除后继承给它的,它现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么

  它现在的颜色是黑+黑。有了这重额外的黑色,原红黑树性质5就能保持不变。现在只要恢复其它性质就可以了,做法还是尽量向根移动和穷举所有可

  能性。

   如果当前节点是红+红,不会影响性质,直接替换就行。

  如果是以下情况,恢复比较简单:另为了方便说明将当前结点设为x。

    1、情况说明:当前结点是红+黑色

      解法,直接把当前结点染成黑色,结束此时红黑树性质全部恢复。

    2、情况说明:当前结点是黑+黑且是根结点

       解法:什么都不做,结束。

    3、情况说明:当前结点是“黑+黑”结点,且不是根。那么有一条路径将会少了一个黑节点。

      1、删除修复情况1:当前结点x是黑+黑且兄弟结点为红色(此时父结点和兄弟结点的子结点分为黑),即处理是否需要将右节点变为黑色。

        (01) 将x的兄弟结点设为“黑色”。
        (02) 将x的父结点设为“红色”。
        (03) 对x的父结点进行左旋。
        (04) 左旋后,重新设置x为兄弟结点。

      变化前:

      

 

      变化后:

      

 

 

 

 

      2、删除修复情况2:当前结点是黑加黑且兄弟是黑色且兄弟结点的两个子结点全为黑色

        (01) 将x的兄弟结点设为“红色”。
        (02) 设置“x的父结点”为“新的x结点”,重新进入算法。

        变化前:

        

 

 

 

        变化后:

        

 

 

 

 

      3、删除修复情况3:当前结点颜色是黑+黑,兄弟结点是黑色,兄弟的左子是红色,右子是黑色

        (01) 将x兄弟结点的左孩子设为“黑色”。
        (02) 将x兄弟结点设为“红色”。
        (03) 对x的兄弟结点进行右旋。
        (04) 右旋后,重新设置x的兄弟结点,之后重新进入算法,即将当前情况转化为情况4

        变化前:

      

 

        变化后:

 

       

 

 

 

 

      4、删除修复情况4:当前结点颜色是黑-黑色,它的兄弟结点是黑色,但是兄弟结点的右子是红色,兄弟结点左子的颜色为任意色。

        (01) 将x父结点颜色 赋值给 x的兄弟结点。
        (02) 将x父结点设为“黑色”。
        (03) 将x兄弟结点的右子节设为“黑色”。
        (04) 对x的父结点进行左旋。
        (05) 设置“x”为“根结点”。        

        变化前:

        

 

        变化后:

 

       

   

红黑树参考连接:https://blog.csdn.net/v_JULY_v/article/details/6105630

 

 

 
 
posted on 2020-04-24 16:59  快牵着我的袜子  阅读(290)  评论(0编辑  收藏  举报