二叉搜索树-红黑树

前面介绍了AVL树,虽然AVL树将二叉树的高度差保证在1,但是实现的太过复杂,因为要不断调整平衡因子。故而要来介绍另外一个用途比较广的结构-红黑树。

红黑树

先来看来红黑树的特性:
1、每个节点非红即黑
2、根节点为黑色
3、不能有连续的红节点
4、每条路径上的黑色节点数相等
5、空节点为黑色

先来想一个问题,红黑树的定义保证它最长路径不会超过最短路径的二倍,那么来想想为什么?

这里写图片描述

节点的结构

因为搜索结构在实际运用当中都是采用这种Key,Value模型,所以这里就先这样实现,后面对RBTree进行封装时,会做一些小的调整。

enum COLOUR{RED,BLACK};

template <class K,class V>
struct RBTreeNode
{
    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;
    RBTreeNode<K, V>* _parent;//因为要涉及到调平衡,所以定义为三叉链
    K _key;
    V _value;
    COLOUR _colour;//枚举类型的颜色属性,每一个节点非红即黑
};

插入

基本的插入和之前的普通搜索树,AVL树都是一样的,先找到待插入位置,然后进行插入即可,主要的不同就是在动态调整。
至于旋转的过程,红黑树和AVL树都是一样的。
插入时需要注意的是,第四条规则:每条路径上的黑色节点数相等,所以我们可以在初始化节点的时候直接默认是红色的节点,这样就可以避免冲突这条规则。

下面这段代码在实现普通搜索树,AVL树,红黑树都是一样的。

        if (_root == NULL)//先判断空树的场景
        {
            Node* _root = new Node(key, value);
            _root->_colour = BLACK;
            return true;
        }

        Node* cur = _root;
        Node* parent = NULL;
        while (cur)
        {
            if (key < cur->_key)
            {
                parent = cur->_parent;
                cur = cur->_left;
            }
            else if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                assert(false);
            }
        }
        //走到这里,找到待插入点
        cur = new Node(key, value);
        if (key < cur->_key)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else if (key > cur->_key)
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        //走到这里说明节点插入进入了,核心的思想就是下面要进行的动态调整

动态调整

下面图示中cur代表当前节点,parent/p代表父节点,uncle/u代表叔叔节点,grandfather/g代表祖父节点。

一共分三种情况:

一:叔叔存在且为红
这里写图片描述
如果是这种情况,直接将父节点和叔叔节点置为红色,也不需要进行旋转,至于为什么将祖父节点置为红色?
1、当前节点的祖父节点是根节点(程序的最后直接将整棵树的根置黑)
2、当前节点的祖父节点不是根

讨论不是根,如图:
这里写图片描述

二:叔叔不存在/存在为黑(cur为p的左)
这里写图片描述

这里的叔叔只要不是存在且为红,就不影响旋转。
以p为根进行一次右单旋(镜像式的左旋类似),然后将p置为黑,g置为红
,因为以g为根的子树上的路径黑色节点个数并没有增加,故不用将旋转后的p在置为红继续向上调整。

三:叔叔不存在/存在为黑(cur为p的右)
这里写图片描述

仔细想一下,这里的第三种是先左旋转化为第二种,再进行左旋后,当前节点的cur和parent对应到第二种的结构中刚好是相反的,所以再进行左旋前/后,要将cur和parent两个节点进行swap,才能转化为第二种结构进行处理。

整个调整过程应该是一个循环来控制:

while(parent && parent->_color == RED);

再进行完动态调整后,最后直接了当将整棵树的根节点变为黑色,从而满足第一条性质。

如果对于旋转部分还不太了解可以先了解AVL树的实现部分,两部分的旋转逻辑一模一样

附上整个简单实现的代码:

#pragma once

#include<iostream>
#include<assert.h>
enum COLOUR{RED,BLACK};

template <class K,class V>
struct RBTreeNode
{
    RBTreeNode(const K& key, const V& value)
    : _left(NULL)
    , _right(NULL)
    , _parent(NULL)
    , _key(key)
    , _value(value)
    , _colour(RED)
    {}

    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;
    RBTreeNode<K, V>* _parent;
    K _key;
    V _value;
    COLOUR _colour;
};

template <class K,class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    RBTree()
    : _root(NULL)
    {}

    bool Insert(const K& key, const V& value)
    {
        if (_root == NULL)
        {
            Node* _root = new Node(key, value);
            _root->_colour = BLACK;
            return true;
        }

        Node* cur = _root;
        Node* parent = NULL;
        while (cur)
        {
            if (key < cur->_key)
            {
                parent = cur->_parent;
                cur = cur->_left;
            }
            else if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                assert(false);
            }
        }
        //走到这里,找到待插入点
        cur = new Node(key, value);
        if (key < cur->_key)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else if (key > cur->_key)
        {
            parent->_right = cur;
            cur->_parent = parent;
        }

        //动态调整节点
        while (parent && parent->_colour == RED )
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                if (uncle && uncle->_colour == RED)
                {
                    parent->_colour = uncle->_colour = BLACK;
                    grandfather->_colour = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_right)
                    {
                        //BUG  第一次旋转后改变了parent和cur的位置,导致第二次旋转后颜色设置错误
                        std::swap(parent, cur);
                        RotateL(parent);
                    }
                    RotateR(grandfather);
                    grandfather->_colour = RED;
                    parent->_colour = BLACK;
                    break;
                }
            }
            else
            {
                Node* uncle = grandfather->_left;
                if (uncle && uncle->_colour == RED)
                {
                    parent->_colour = uncle->_colour = BLACK;
                    grandfather->_colour = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        std::swap(parent, cur);
                        RotateR(parent);
                    }
                    RotateL(grandfather);
                    grandfather->_colour = RED;
                    parent->_colour = BLACK;
                    break;
                }
            }
        }
        _root->_colour = BLACK;
        return true;
    }

    bool CheckBalance()
    {
        size_t k = 0;
        if (NULL == _root)
            return true;        

        if (RED == _root->_colour)
        {   
            std::cout << "根节点为红" << " ";
            return false;
        }

        size_t BlankCount = 0;
        Node* cur = _root;
        while (cur)
        {
            if (BLACK == cur->_colour)
            {
                BlankCount++;
                cur = cur->_left;
            }
        }


        _CheckBalance(_root,BlankCount,k);
    }


    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_right;
        Node* pparent = parent->_parent;

        parent->_right = subR->_left;
        if (subRL)
        {
            subRL->_parent = parent;
        }
        subR->_left = parent;
        parent->_parent = subR;

        while (NULL == pparent)
        {
            _root = subR;
            subR->_parent = NULL;
        }
        if (parent == pparent->_left)
            pparent->_left = subR;
        else if (parent == pparent->_right)
            pparent->_right = subR;

        subR->_parent = pparent;

        parent = subR;
    }

    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* pparent = parent->_parent;

        parent->_left = subLR;
        if (subLR)
        {
            subLR->_parent = parent;
        }
        subL->_right = parent;
        parent->_parent = subL;
        while (NULL == pparent)
        {
            _root = subL;
            subL->_parent = NULL;
        }
        if (parent == pparent->_left)
            parent->_left = subL;
        else if (parent == pparent->_right)
            parent->_right = subL;

        subL->_parent = pparent;
        parent = subL;
    }

    bool _CheckBalance(Node* root, size_t BlankCount, size_t k)
    {
        if (NULL == root)
            return true;
        if (BLACK == root->_colour)
            k++;
        Node* parent = root->_parent;
        if (root->_colour == RED && parent->_colour == RED)
        {
            std::cout << "相邻的红节点" << " ";
            return false;
        }

        if (root->_left == NULL && root->_right == NULL)
        {
            if (k != BlankCount)
            {
                std::cout << "黑色数量不相等" << " ";
                return false;
            }

            return true;
        }

        return (_CheckBalance(root->_left, BlankCount, k) && _CheckBalance(root->_right, BlankCount, k));
    }
private:
    Node* _root;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值