数据结构之红黑树

本文深入探讨了红黑树的本质——一种近似平衡的搜索树,并详细阐述了红黑树的特征。内容包括红黑树结点的插入过程,以及在插入后如何通过调整保持红黑树的特性,涉及单旋、双旋等操作。同时,文章提供了判断树是否符合红黑树特征的IsBlance函数的实现。
摘要由CSDN通过智能技术生成

红黑树
本质是一棵近似平衡的搜索树,当然,想要了解红黑树的前提是你足够了解搜索树和AVL(高度平衡的二叉搜索树);

红黑树的特征
(注意,本文所讲内容时刻围绕这五条特征,请仔细阅读)

1. 每个节点,不是红色就是黑色的
 2. 根节点是黑色的 
 3. 如果一个节点是红色的,则它的两个子节点是黑色的 
 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。 
 5. 空节点都是黑色的

下面是一颗典型的红黑树特例,结合红黑书的特点看图:
这里写图片描述

接下来的内容就围绕红黑树的插入结点展开;

注意:时刻保持红黑树的特征

红黑树结点的结构组成:

enum Color
{
    RED,
    BLACK
};

template<class K,class V>
struct RBNode
{
    RBNode<K,V>* _left;
    RBNode<K,V>* _right;
    RBNode<K,V>* _parent;

    K _key;//关键码,在树中具有惟一性;
    V _value;//携带的值,可有可无;
    Color _color;//颜色

    RBNode(){}
    RBNode(const K& key,const V& value)
        :_left(NULL)
        ,_right(NULL)
        ,_parent(NULL)
        ,_key(key)
        ,_value(value)
        ,_color(RED)//新入结点初始颜色为红
    {}
};

1.分析我们插入节点的颜色应该初始为黑还是红?
答案是红,为什么?

结合红黑树的特征,每条路径上的黑色节点的数目必须相等,如果新插入结点的颜色为黑色的话,岂不是每次都得进行调整,维持红黑树的特性;
而新插入节点的颜色如果为红色的话,则可以省事一些;

2.寻找插入结点的位置,和搜索树一样(如果不知道搜索树,赶紧了结),先将结点连在树上,再进行调整;这里只列出代码;

//首先依据搜索数特性找到插入元素的位置;
        if(_root == NULL)
        {
            _root = new Node(key,value);
            _root->_color = BLACK;
            return true;
        }

        Node* cur = _root;
        Node* parent = NULL;
        while(cur)
        {
            parent = cur;

            if(cur->_key < key)
                cur = cur->_right ;

            else if(cur->_key > key)
                cur = cur->_left ;
            //注意,如果元素的key已经存在,则插入失败;
            else 
                return false;
        }
         //找到插入位置
        cur = new Node(key,value);

        if(parent->_key > key)
            parent->_left = cur;
        else
            parent->_right = cur;

        cur->_parent = parent;

(注意:接下来我们约定只以插入结点在根节点的左边为例,右边以此类推
3.新结点已经连在了树上,现在要考虑的情况又变味怎样进行调整,我们在这里分为5种情况:

我们在这里进行图示的一些约定:‘c’ 代表新插入的结点或调整上来的结点,‘p’代表父亲结点,‘u’代表叔叔结点,‘g’代表组父节点;三角形代表一颗子树或者一颗空树,视情况而定!(至于什么是叔叔结点,父亲结点,祖父结点之类,相信大家观察一下就可以理解!)

注意:一切调整的前提都是建立在红黑树的特征被改变了的情况下!
(依据红黑树的特征)
1. 新入结点的是根节点,不需要进行调整,只要改变颜色为黑就好;
2. 新入结点的父节点为黑结点,不会改变红黑树的特征,不需要进行调整,颜色也不需要改变;
3. 新入结点的叔叔存在且为红结点(如图)
这里写图片描述
4. 叔叔结点不存在;
这里写图片描述
5. 叔叔结点存在且为黑;
这里写图片描述

4.上述的情况5又可以分为,新插入节点在父亲的左边或者右边两种情况;这就涉及到单旋和双旋的情况,知道AVL树的同学应该有些了解,但是这里的双旋又和AVL树的双旋略有不同;

上述五种情况只有后三种需要进行调整,调整的目的在于通过旋转维持红黑树的特征;

  • 叔叔存在且为红的调整:只需要将U和P结点的颜色变为黑,g变为红即可,保证每条路径上的黑节点个数和以前一样,此时应把g当作新的C结点继续向上调整,因为,我们所给出的树可能只是一颗子树,如果g是根节点,那么只需要再把g的颜色变为黑即可;如下图:
    这里写图片描述

    -叔叔结点不存在的情况其实可以并入第三种情况的一种单旋的情况,区别在于,如果叔叔节点不存在,则C一定是新插入的结点,而叔叔结点存在且为黑,则C一定是调整上来的结点,;
    这里写图片描述

结合上图的情况进行一次右单旋,g变为p的右,而原来p的右子树变为g的左子树,改变g和p的颜色,以保持红黑树的特征,如下图:
这里写图片描述

绿框的部分表示旋转的轴!

  • 叔叔结点存在且为黑,我们直接选择这种情况中的新插入节点在父节点右边的情况,在父亲结点左边的情况类似叔叔结点为空的情况;

如下图,需要进行双旋的情况:
这里写图片描述

先以p和c为轴,进行一次左单选,再以c和p为轴进行一次右单旋,这里需要注意的是,第一次旋转的时候,c和p的位置发生了变化,再最后改变g和p的颜色时,就有问题了,所以,在代码实现的时候,在第一次旋转之后,我们应该对p和c的指针进行交换,切记!!!
这里写图片描述

注意:一次的调整并不代表结束,因为g有可能一开始就是一颗子树,所以,需要继续向上进行判断调整,直到不用调整

5.实现代码
在实现代码的时候,我还加入了IsBlance这个函数,来判断树是否符合红黑树的特征,判断的根据还是红黑树的五个特征;

完整代码的连接:RBTree 完整代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值