二叉搜索树、平衡二叉搜索树和红黑树

文章目录

    • 一. 二叉搜索树(Binary Sort Tree)
    • 二. 二叉平衡搜索树(AVL)
    • 三. 红黑树

一. 二叉搜索树(Binary Sort Tree)

二叉搜索树,又称为二叉排序树(二叉查找树),它或许是一棵空树,或许是具有一下性质的二叉树:

1.若它的左子树不为空,则左子树上所有的节点的值小于根节点的值
2.若它的右子树不为空,则右子树上所有的节点的值都大于根节点的值
3.它的左右子树也分别是二叉搜索树

在这里插入图片描述
二叉搜索树的这种特性,使得我们在此二叉树上查找某个值就很方便了,从根节点开始,若要寻找的值小于根节点的值,则在左子树上去找,反之则去右子树查找,直到找到与值相同的节点。插入节点也是一样的道理,从根节点从发,所要插入的值,若小于根节点则去左子树寻找该节点所对应的位置,反之则去右子树寻找,直到找到该节点合适的位置。
在这里插入图片描述

二. 二叉平衡搜索树(AVL)

前面提到二叉搜索树,二叉搜索树的特性便于我们进行查找插入删除等一系列操作,其时间复杂度为O(logn),但是遇见最差的情况,比如以下这棵树:
在这里插入图片描述
这棵树,说是树,其实已经退化成链表了,但从概念上来看,它仍是一棵二叉搜索树,只要我们按照逐次增大,如1、2、3、4、5、6的顺序构造一棵二叉搜索树,如上图。那么插入的时间复杂度就变成了O(n),导致这种糟糕的情况原因是因为这棵树极其不平衡,右树的重量远大于左树,因此提出平衡二叉搜索树的结构,称为AVL树。

平衡二叉树,它能保持二叉树的高度平衡,降低二叉树的高度,减少树的平均查找长度。

AVL树的性质:

1. 左子树与右子树高度之差的绝对值不超过1
2. 树的每个左子树和右子树都是AVL树
3. 每一个节点都有一个平衡因子(balance factor),任一节点的平衡因子是-1,0,1(每一个节点的平衡因子=右子树高度-左子树高度)

做到了这点,这棵树看起来就比较平衡了,那么如何生成一棵AVL树呢?算法相对来说复杂,随着新节点的加入,树自动调整自身结构,达到新的平衡状态,这就是我们想要的AVL树。我们分析下,为什么树会失衡?是由于插入了一个新的元素。

在AVL树中,插入一个节点的过程如下:

AVL树插入节点:
1. AVL树首先是二叉搜索树,我们要根据二叉搜索树的插入节点方式进行插入
2. AVL树有判断该树是否平衡的因子,我们要根据平衡因子来对树进行选择调整
具体步骤:
1. 判断该树是不是NULL,若为NULL,直接插入
2. 若不为NULL, 找到要插入节点的位置(用pParent标记双亲,方便插入节点)pCur
3. 插入节点pCur
4. 更新pParent的平衡因子,然后判断该树是否需要调整
      a.若更新后pParent平衡因子为0的话,pParent在插入之前只有左孩子或者右孩子,此时树的高度不变,该树仍然为AVL树。
      b.若更新后的pParent平衡因子为1或者-1的话,pParent在插入节点前是叶子节点,此时树的高度可能发生改变,我们需要从pParent节点开始向上判断调整其祖先节点。
      c.若平衡因子不满足上面的俩种情况,说明该树已经不平衡,需要调整,具体情况见下面,局部调整完后,上面的树已经满足AVL树,此时退出即可。

(在下图中,c)插入节点后,调整平衡因子 -> 插入节点之后->(2)最后一个图,其Parent的bf应为1)
二叉搜索树、平衡二叉搜索树和红黑树_第1张图片插入节点代码实现如下:

template 
bool AVLTree::AVLInsert(K key, V val){
    //1.根节点为空,直接插入
    if (_root == NULL) {
        _root = new Node(key, val);
        return true;
    }
    //2.根节点不为空
    else{
        Node* cur = _root;
        Node* parent =NULL;
        //a)找到要插入节点的位置
        while (cur) {
            parent = cur;
            if (cur->_key > key)
                cur = cur->_left;
            else if (cur->_key < key)
                cur = cur->_right;
            else
                return false;   //不允许出现重复元素的节点
        }
        //b)插入新节点
        cur = new Node(key, val);
        if (parent->_key>key){
            parent->_left = cur;
            cur->_parent = parent;
        }
        else{
            parent->_right = cur;
            cur->_parent = parent;
        }
        //c)插入完成后,调整平衡因子
        while (parent){
            if (cur == parent->_left)//插入节点在左子树父节点bf--,反之++
            parent->_bf--;
        else
            parent->_bf++;
            //1)插入新节点后,parent->bf==0;说明高度没变,平衡,返回
            if (parent->_bf == 0)
                break;
            //2)插入节点后parent->_bf==-1||parent->_bf==1;说明子树高度改变,则继续向上调整
            else if (parent->_bf == -1 || parent->_bf == 1){
                cur = parent;
                parent = parent->_parent;
            }
            //3)插入节点后parent->_bf==-2||parent->_bf==2;说明已经不平衡,需要旋转
            else{
                if (parent->_bf == 2){
                    if (cur->_bf == 1)
                        RotateL(parent);
                    else// (cur->_bf == -1)
                        RotateRL(parent);
                }
                else//parent->_bf == -2 {
                    if (cur->_bf == -1)
                        RotateR(parent);
                    else// (cur->_bf == 1)
                        RotateLR(parent);
                }
                break;
            }

        }//end while (parent)
        return true;
    }
}

当树不平衡时,我们需要做出旋转调整,有四种调整方法,分别为:1. 左单旋 2.右单旋 3.左右单旋 4.右左单旋, 具体调整方法见:https://blog.csdn.net/tanrui519521/article/details/80935348

三. 红黑树

红黑树是自平衡二叉树中的一种,在插入元素时,二叉树会自动调整元素的位置,使树的倆叉维持平衡,红黑树最普遍的用途就是用来实现关联数组。
二叉搜索树、平衡二叉搜索树和红黑树_第2张图片
红黑树遵守5个基本命题:
1.节点是红色或者黑色;
2.根是黑色;
3.所有叶子都是黑色(叶子是NIL节点,空节点);
4.每个红色节点的俩个子节点都是黑色。(从每个叶子到根的所有路径上不能有俩个连续的红色节点);
5.从任一节点到其每个叶子的所有简单路径都包含相同的黑色节点。
注意到命题5,“从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点”,这一点就保证了,红黑树的树高由于每个分支上黑色节点数目一定而维持平衡。

你可能感兴趣的:(C/C++,平衡二叉搜索树,红黑树)