C++:红黑树

概念

红黑树是一种二叉搜索树,一般的二叉搜索会发生不平衡现象,导致搜索效率下降,于是学者们开始探索如何让二叉搜索树保持平衡,这种树叫做自平衡二叉搜索树。起初学者发明了AVL树,其通过一定算法保持了二叉搜索树的严格平衡,不久后Rudolf Bayer发明了红黑树,红黑树的平衡是较为宽泛的,为了保持平衡,红黑树付出的代价比AVL树更小。

红黑树的要求如下:

红黑树中,最长路径的长度不会超过最短路径的两倍

先解释一下路径的概念:从根走到nullptr
有不少人认为路径是从根走到叶子节点,这是不正确的。

红黑树用了五条规则来限制一棵树,从而达到以上要求:

  1. 每个节点不是红色就是黑色
  2. 根节点一定是黑色
  3. 不可以出现连续的红色节点(黑色可以连续出现)
  4. 每一条路径都包含相同数目的黑色节点
  5. nullptr视为黑色节点

只要满足以上五个条件,那么这棵树就是一颗红黑树,而且满足最长路径的长度不会超过最短路径的两倍。


实现

基本结构

首先我们要在节点中加入一个成员来表示节点的颜色,颜色有红黑和黑色两种状态,这里我使用枚举来区分两者:

enum Colour
{
    RED,
    BLACK
};

 在某些红黑树的实现中,使用bool值来表示红黑颜色,这也是可以的,但是本博客以枚举来表示颜色。

节点类:

template<class K, class V>
struct RBTreeNode
{
    RBTreeNode* _left;
    RBTreeNode* _right;
    RBTreeNode* _parent;
    pair<K, V> _kv;
    Colour _col;
};

对于一棵红黑树,其所有路径的黑色节点数目都相同,如果我们在某一条路径末尾插入了黑色节点,那么整棵树的所有其它路径都会少一个黑节点。而插入红色节点只影响当前路径,所以新节点应该是红色节点。 

构造函数:

RBTreeNode(const pair<K, V>& kv)
    : _left(nullptr)
    , _right(nullptr)
    , _parent(nullptr)
    , _kv(kv)
    , _col(RED)//初始化为红节点
{}

接着就是红黑树本体,类中只存储一个根节点_root

template<class K, class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
private:
	Node* _root = nullptr;
}

插入

那么我们先写出当基本的二叉搜索树的插入代码逻辑,既然要插入,那么就要先找到合适的位置插入

bool Insert(const pair<K, V>& kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        _root->_col = BLACK;//保持根为黑节点
    }

    Node* cur = _root;
    Node* parent = nullptr;

    while (cur)
    {
        if (cur->_kv.first < kv.first)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_kv.first > kv.first)
        {
            parent = cur;
            cur = cur->_left;
        }
        else
        {
            return false;
        }
    }

    cur = new Node(kv);

    if (parent->_kv.first > kv.first)
        parent->_left = cur;
    else
        parent->_right = cur;

    cur->_parent = parent;
   
   //调整红黑树
   //......
   //......
   //......
   
    return true;
}

对于红黑树的插入,我们需要关注新节点的父亲parent,祖父grandfather,叔叔uncle三个节点: 

  1. 先根据父亲节点的颜色,来判断是否需要调整

父亲节点为黑色:

新插入的节点默认为红色,所以新插入节点不会影响路径上黑色节点的数目,而parent是黑节点,我们也没有出现连续的红色节点,所以这种情况无需任何调整,直接插入就可以。 

父亲节点为红色: 

 

如果父亲节点为红色,我们就会出现连续的红色节点,这时我们就需要进行调整了 

 当parent为红色,我们就需要再根据uncle的颜色,将插入分类两类:uncle为红色以及uncle为黑色

值得注意的是:由于parent是红色节点,此时的grandfather一定是黑色节点,因为不能出现连续红色节点。


uncle为红色节点

uncle节点为红色,此时需要进行变色

 我们将grandfather变为了红色,这有可能会影响到上一层节点,所以我们要写一个while循环,来反复向上检查。

当前代码如下:

while (parent && parent->_col == RED)//只有parent为红,才更新 (parent可能不存在)
{
    Node* grandfather = parent->_parent;
    if (parent == grandfather->_left)
    {
        Node* uncle = grandfather->_right;

        //uncle存在且为红节点
        if (uncle && uncle->_col == RED)
        {
            parent->_col = uncle->_col = BLACK;
            grandfather->_col = RED;

            cur = grandfather;
            parent = cur->_parent;
        }
        else//uncle为黑节点 
        {
            //其它处理
        }
    }
    else
    {
        Node* uncle = grandfather->_left;

        //uncle存在且为红节点
        if (uncle && uncle->_col == RED)
        {
            parent->_col = uncle->_col = BLACK;
            grandfather->_col = RED;

            cur = grandfather;
            parent = cur->_parent;
        }
        else//uncle为黑节点 
        {
        	//其它处理
        }
    }
}

_root->_col = BLACK;//在循环内部不判断root情况,统一处理

uncle为黑色节点

由于红黑树中,nullptr也算作黑色节点,所以uncle为黑色分为以下两种情况:

  1. uncle为空指针
  2. uncle不为空指针

如果 uncle为空指针,那么cur一定是新插入的节点。 

因为如果cur不是新插入的节点,那么curparent一定有一个原先是黑色节点,不然会出现连续的红色节点。但是如果curparent有一个是黑色节点,那么grandfather的左子树就比右子树多出一个黑节点,这就违背了红黑树规则。

如果 uncle不为空指针,那么cur一定是从黑色节点变成的红色节点(不是新插入的)。

因为如果uncle存在,那么grandfather的右子树就存在一个黑节点,而parent是红节点,所以curparent的右子树中都至少有一个黑节点,才能保证每一条路径黑节点数目相同。因此cur原先一定是黑节点,是因为cur下层插入了新节点,然后通过while循环向上走,影响到了当前层

 

对于这种uncle为黑色的情况,我们需要通过旋转+变色来维持红黑树。

旋转又分为单旋和双旋:

curparent的关系和parentgrandfather的关系一致时,需要进行单旋

curparent的左子树,parentgrandfather的左子树,关系一致。
我们需要对其进行右单旋+变色:

curparent的关系和parentgrandfather的关系不一致时,需要进行双旋 

以上结构中,curparent的左子树,parentgrandfather的右子树,关系不一致,要进行双旋。 

 以上单旋和双旋的变色,看似复杂,其实最后都是把新根的颜色变为黑色,新根的左右子树变为红色。由于我们旋转后,新根都是黑节点,所以不会影响上层,可以直接跳出循环。


parent == grandfather->_left

else//uncle为黑节点 (旋转)
{
    if (cur == parent->_left)
    {
        RotateR(grandfather);//右单旋
        parent->_col = BLACK;//变色
        grandfather->_col = RED;//变色
    }
    else
    {
        RotateL(parent);//左右双旋 - 左单旋
        RotateR(grandfather);//左右双旋 - 右单旋

        cur->_col = BLACK;//变色
        grandfather->_col = RED;//变色
    }

    break;//旋转后一定平衡
}

parent == grandfather->_right

else//uncle为黑节点 (旋转)
{
    if (cur == parent->_right)
    {
        RotateL(grandfather);//左单旋
        parent->_col = BLACK;//变色
        grandfather->_col = RED;//变色
    }
    else
    {
        RotateR(parent);//右左双旋 - 右单旋
        RotateL(grandfather);//右左双旋 - 左单旋

        cur->_col = BLACK;//变色
        grandfather->_col = RED;//变色
    }

    break;//旋转后一定平衡
}

insert总代码:        

bool Insert(const pair<K, V>& kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        _root->_col = BLACK;//保持根为黑节点
    }

    Node* cur = _root;
    Node* parent = nullptr;

    while (cur)
    {
        if (cur->_kv.first < kv.first)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_kv.first > kv.first)
        {
            parent = cur;
            cur = cur->_left;
        }
        else
        {
            return false;
        }
    }

    cur = new Node(kv);

    if (parent->_kv.first > kv.first)
        parent->_left = cur;
    else
        parent->_right = cur;

    cur->_parent = parent;

    while (parent && parent->_col == RED)//只有parent为红,才更新 (parent可能不存在)
    {
        Node* grandfather = parent->_parent;
        if (parent == grandfather->_left)
        {
            Node* uncle = grandfather->_right;

            //uncle存在且为红节点
            if (uncle && uncle->_col == RED)
            {
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;

                cur = grandfather;
                parent = cur->_parent;
            }
            else//uncle不存在或为黑节点 (旋转)
            {
                if (cur == parent->_left)
                {
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
                    RotateL(parent);
                    RotateR(grandfather);


                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }

                break;//旋转后一定平衡
            }
        }
        else
        {
            Node* uncle = grandfather->_left;

            //uncle存在且为红节点
            if (uncle && uncle->_col == RED)
            {
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;

                cur = grandfather;
                parent = cur->_parent;
            }
            else//uncle不存在或为黑节点 (旋转)
            {
                if (cur == parent->_right)
                {
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
                    RotateR(parent);
                    RotateL(grandfather);

                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }

                break;//旋转后一定平衡
            }
        }
    }

    _root->_col = BLACK;//在循环内部不判断root情况,统一处理

    return true;
}

 

总代码展示

红黑树总代码:
RBTree.h

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

enum Colour
{
    RED,
    BLACK
};

template<class K, class V>
struct RBTreeNode
{
    RBTreeNode* _left;
    RBTreeNode* _right;
    RBTreeNode* _parent;
    pair<K, V> _kv;
    Colour _col;

    RBTreeNode(const pair<K, V>& kv)
        : _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _kv(kv)
        , _col(RED)
    {}
};

template<class K, class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = BLACK;//保持根为黑节点
        }

        Node* cur = _root;
        Node* parent = nullptr;

        while (cur)
        {
            if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }

        cur = new Node(kv);

        if (parent->_kv.first > kv.first)
            parent->_left = cur;
        else
            parent->_right = cur;

        cur->_parent = parent;

        while (parent && parent->_col == RED)//只有parent为红,才更新 (parent可能不存在)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;

                //uncle存在且为红节点
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//uncle不存在或为黑节点 (旋转)
                {
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateL(parent);
                        RotateR(grandfather);

                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }

                    break;//旋转后一定平衡
                }
            }
            else
            {
                Node* uncle = grandfather->_left;

                //uncle存在且为红节点
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//uncle不存在或为黑节点 (旋转)
                {
                    if (cur == parent->_right)
                    {
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateR(parent);
                        RotateL(grandfather);

                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }

                    break;//旋转后一定平衡
                }
            }
        }

        _root->_col = BLACK;//在循环内部不判断root情况,统一处理

        return true;
    }
    
    //左单旋
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;

        parent->_right = subRL;
        if (subRL)
            subRL->_parent = parent;

        subR->_left = parent;
        Node* ppNode = parent->_parent;
        parent->_parent = subR;

        if (parent == _root)
        {
            _root = subR;
            subR->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
                ppNode->_left = subR;
            else
                ppNode->_right = subR;

            subR->_parent = ppNode;
        }
    }

    //右单旋
    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;

        parent->_left = subLR;
        if (subLR)
            subLR->_parent = parent;

        subL->_right = parent;
        Node* ppNode = parent->_parent;
        parent->_parent = subL;

        if (parent == _root)
        {
            _root = subL;
            subL->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
                ppNode->_left = subL;
            else
                ppNode->_right = subL;

            subL->_parent = ppNode;
        }
    }

    size_t Size()
    {
        return _Size(_root);
    }

    size_t _Size(Node* root)
    {
        if (root == nullptr)
            return 0;;

        return _Size(root->_left) + _Size(root->_right) + 1;
    }

    Node* Find(const K& key)
    {
        Node* cur = _root;

        while (cur)
        {
            if (cur->_kv.first < key)
            {
                cur = cur->_right;
            }
            else if (cur->_kv.first > key)
            {
                cur = cur->_left;
            }
            else
            {
                return cur;
            }
        }

        return nullptr;
    }

    //中序
    void InOrder()
    {
        _InOrder(_root);
        cout << "end" << endl;
    }

    int Height()
    {
        return _Height(_root);
    }

private:
    //中序
    void _InOrder(Node* root)
    {
        if (root == nullptr)
            return;

        _InOrder(root->_left);
        cout << root->_kv.first << " - ";

        _InOrder(root->_right);
    }

    //求高度
    int _Height(Node* root)
    {
        if (root == nullptr)
            return 0;

        return max(Height(root->_left), Height(root->_right)) + 1;
    }

    Node* _root = nullptr;
};

 

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 15:54:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 15:54:02       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 15:54:02       58 阅读
  4. Python语言-面向对象

    2024-07-12 15:54:02       69 阅读

热门阅读

  1. 2.HTML学习

    2024-07-12 15:54:02       22 阅读
  2. “存算分离“和“湖仓一体“

    2024-07-12 15:54:02       17 阅读
  3. 对数据采集、数据存储和数据处理流程

    2024-07-12 15:54:02       18 阅读
  4. 7.8作业

    7.8作业

    2024-07-12 15:54:02      22 阅读
  5. 使用GeographicLib在C++中进行地理坐标转换

    2024-07-12 15:54:02       22 阅读
  6. 使用Gunicorn提高Web应用的多核并发处理能力

    2024-07-12 15:54:02       24 阅读
  7. Vue使用socket实现实时通信

    2024-07-12 15:54:02       25 阅读