红黑树具体实现

红黑树

可以在 O(logn) 时间内做查找,插入和删除操作。
红黑树的性质:

  • 每个结点要么是红的,要么是黑的;
  • 根结点是黑色的;
  • 如果一个结点是红色的,则它的两个孩子都是黑色的;
  • 对于任意一个结点,其到叶子结点的每条路径上都包含相同数目的黑色结点

具体实现与代码

和 AVL 树通过约束左右子树高度不同,红黑树是通过它的四条性质来实现 “平衡状态”,在插入结点或者删除结点时,可能会造成某个结点违反了上述的某条性质,那么红黑树的做法就是通过 “重新着色” 和 “旋转” 两种方式使之重新符合性质。

左旋

在这里插入图片描述

void RBTree::ratate_left(Node * x)
{
    Node * y = x->right;
	//1
    x->right = y->left;
    if (y->left)
        y->left->parent = x;
	//2
    y->parent = x->parent;
    if (x == root())
        root() = y;
    else if (x == x->parent->left)
        x->parent->left = y;
    else
        x->parent->right = y;
	
	//3
    y->left = x;
    x->parent = y;
}

右旋
在这里插入图片描述

void RBTree::rotate_right(Node * x)
{
    Node * y = x->left;
	//1
    x->left = y->right;
    if (y->right)
        y->right->parent = x;
    
    //2
    y->parent = x->parent;
    if (x == root())
        root() = y;
    else if (x == x->parent->right)
        x->parent->right = y;
    else
        x->parent->left = y;
	//3
    y->right = x;
    x->parent = y;
}

插入操作

首先,我们把新插入的结点着色为红色。将插入的结点着色为红色,不会违背 “性质 4”。而少违背一条性质,就意味着我们需要处理的情况越少。

我们来看看插入结点会遇到哪几种情况,分析发现,一共有三种:

  • 被插入结点是根结点,那我们把此结点涂为黑色就行了;
  • 被插入结点的父亲结点是黑色的,那么什么也不需要做,结点被插入后,仍然是红黑树。
  • 被插入结点的父亲结点是红色的,那么此时是违背 “性质 3” 的。

“被插入结点的父亲结点是红色的” 这种情况下,被插入结点是一定存在非空祖父(即父亲的父亲)结点的。那么此时这种情况可以进一步再划分为 6 种情况,因为涉及到镜像操作,父节点是祖父节点的左子树的情况时:我们只需理解其中的 3 种情况即可,为方便叙述,我们把 “被插入结点” 称为 “当前结点”,

  • Case 1 :当前结点的父亲为红色,叔叔存在且也是红色

在这里插入图片描述
图中,“结点 1” 为 “当前结点”。那么我们的处理策略就是:

将 “父亲结点” 改为黑色;
将 “叔叔结点” 改为黑色;
将 “祖父结点” 改为红色;
将 “祖父结点” 设为 “当前结点”,继续进行操作。

  • Case 2:当前结点的父亲为红色,叔叔不存在或为黑色,且当前结点是其父亲的右孩子
    在这里插入图片描述
    图中,“结点 2” 为 “当前结点”。那么我们的处理策略就是:

将 “父亲结点” 设为 “当前结点”;
以 “新的当前结点” 为支点进行左旋。
转为case3

  • Case 3:当前结点的父亲为红色,叔叔不存在或为黑色,且当前结点是其父亲的左孩子

在这里插入图片描述
图中,“结点 1” 为 “当前结点”。那么我们的处理策略就是:

  • 将 “父亲结点” 改为黑色;
  • 将 “祖父结点” 改为红色;
  • 以 “祖父结点” 为支点进行右旋。

删除操作

为方便叙述,我们把 “被删结点” 称为 “原先结点”,用来替换 “被删结点” 的结点称为 “当前结点”。

  1. 没有左右子树的情况
    在这里插入图片描述

  2. 有左子树或者右子树

在这里插入图片描述
3. 有左子树且有右子树

在这里插入图片描述

我们来看看删除一个结点会遇到哪几种情况,分析发现,一共有四种:

  • “原先结点” 为黑色,“当前结点” 为红色,那么我们把 “原先结点” 删掉后,拿 “当前结点” 去替换它并修改颜色为黑色即可;.
  • “原先结点” 为黑色,“当前结点” 为黑色,这种情况比较复杂,待会再说;
  • “原先结点” 为红色,“当前结点” 为红色,那么我们把 “原先结点” 删掉后,直接拿 “当前结点” 去替换它即可;
  • “原先结点” 为红色,“当前结点” 为黑色,那么我们把 “原先结点” 删掉后,再拿 “当前结点” 去替换它。我们发现,此时 “原先结点” 位置是满足红黑树性质的,但是由于 “当前结点” 被拿走,“当前结点” 位置可能就会违背红黑树性质。分析发现,此时的 “当前结点” 不就是上面 “情况 1” 和 “情况 2” 中所讲的 “原先结点”!那么当前的这种情况直接就变成了 “情况 1” 或 “情况 2”。

最后,我们看下上述的 “情况 2”。这种情况可以进一步再划分为 8 种情况,因为涉及到镜像操作,所以我们只需理解其中一边镜像的 4 种情况即可(注意,下面的图片上,“原先结点” 已被删除,故未画出,我们只画出了 “当前结点”):

  • Case 1:当前结点是黑色,兄弟结点是红色

在这里插入图片描述

图中,“结点 1” 为 “当前结点”。观察上图,因 “原先结点” 已被删除,故原来每条路径上应该是 3 个黑色结点(右侧路径未画完全),此时左侧少了一个,那么我们的处理策略就是:

  • 将 “兄弟结点” 改为黑色;
  • 将 “父亲结点” 改为红色;
  • 以 “父亲结点” 为支点进行左旋;
  • 左旋后,重新设置 “兄弟结点”。

我们发现最左侧路径依旧是 2 个黑色结点,说明当前状态并不满足红黑树性质。其实,这是进入了下面的 Case 2,Case 3,和 Case 4 阶段了,请继续往下看。

Case 2:当前结点是黑色,兄弟结点是黑色,两个孩子为空或是黑色

在这里插入图片描述

图中,“结点 1” 为 “当前结点”。观察上图,因 “原先结点” 已被删除,故原来每条路径上应该是 2 个黑色结点,此时左侧少了一个,那么我们的处理策略就是:

  • 将 “兄弟结点” 改为红色;
  • 将 “父亲结点” 设置为新的 “当前结点”,继续进行操作。

但是我们发现只要把 “结点 2” 着色为黑色不就行了么,这也就是erase_rebalance代码最后出现if(x) x->color = black;的缘由之一(x指向的是 “当前结点”)。

Case 3:当前结点是黑色,兄弟结点是黑色,兄弟结点的左孩子是红色,右孩子是为空或是黑色

在这里插入图片描述

图中,“结点 1” 为 “当前结点”。观察上图,因 “原先结点” 已被删除,故原来每条路径上应该是 2 个黑色结点,此时左侧少了一个,那么我们的处理策略就是:

  • 将 “兄弟结点” 的左孩子改为黑色;
  • 将 “兄弟结点” 改为红色;
  • 以 “兄弟结点” 为支点进行右旋;
  • 右旋后,重新设置 “当前结点” 的 “兄弟结点”。
    处理完后,我们发现图中最左路径只有 1 个黑色结点,说明当前状态不满足红黑树性质。其实这是进入了 Case 4。

Case 4:当前结点是黑色,兄弟结点是黑色,兄弟结点的右孩子是红色,左孩子为空或红黑皆可
在这里插入图片描述

图中,“结点 1” 为 “当前结点”。观察上图,因 “原先结点” 已被删除,故原来每条路径上应该是 2 个黑色结点,此时左侧少了一个,那么我们的处理策略就是:

  • 将 “父亲结点” 的颜色赋给 “兄弟结点”;
  • 将 “父亲结点” 改为黑色;
  • 将 “兄弟结点” 的右孩子改为黑色;
  • 以 “父亲结点” 为支点进行左旋;

总体代码

/**
*
* author 刘毅(Limer)
* date   2017-08-20
* mode   C++
*/
#include <iostream>
#include <algorithm>
using namespace std;

enum { red = 0, black = 1 };

struct Node
{
    int key;
    bool color;
    Node * parent;
    Node * left;
    Node * right;
    Node(int key = 0)
    {
        this->key = key;
        this->color = red;
        this->parent = this->left = this->right = nullptr;
    }
};

class RBTree
{
private:
    Node * header;
private:
    void ratate_left(Node * x);
    void rotate_right(Node * x);
    void destroy(Node * node);
    Node *& root();
    void insert_rebalance(Node * x);
    void erase_rebalance(Node * z);
    void in_order(Node * node);
public:
    RBTree();
    ~RBTree();
    Node * insert(int key);
    Node * find(int key);
    void erase(int key);
    void print();
};

void RBTree::ratate_left(Node * x)
{
    Node * y = x->right;

    x->right = y->left;
    if (y->left)
        y->left->parent = x;
    y->parent = x->parent;

    if (x == root())
        root() = y;
    else if (x == x->parent->left)
        x->parent->left = y;
    else
        x->parent->right = y;

    y->left = x;
    x->parent = y;
}

void RBTree::rotate_right(Node * x)
{
    Node * y = x->left;

    x->left = y->right;
    if (y->right)
        y->right->parent = x;
    y->parent = x->parent;

    if (x == root())
        root() = y;
    else if (x == x->parent->right)
        x->parent->right = y;
    else
        x->parent->left = y;

    y->right = x;
    x->parent = y;
}

void RBTree::destroy(Node * node)
{
    if (node == nullptr)
        return;
    destroy(node->left);
    destroy(node->right);
    delete node;
}

Node *& RBTree::root()
{
    return header->left;
}

void RBTree::insert_rebalance(Node * x)
{
    x->color = red;

    while (x != root() && x->parent->color == red)
    {
        if (x->parent == x->parent->parent->left)
        {
            Node * y = x->parent->parent->right;

            if (y && y->color == red)           // Case 1
            {
                x->parent->color = black;
                y->color = black;
                x->parent->parent->color = red;
                x = x->parent->parent;
            }
            else
            {
                if (x == x->parent->right)      // Case 2
                {
                    x = x->parent;
                    ratate_left(x);
                }

                x->parent->color = black;       // Case 3
                x->parent->parent->color = red;
                rotate_right(x->parent->parent);
            }
        }
        else  // same as above, just left <-> right
        {
            Node * y = x->parent->parent->left;

            if (y && y->color == red)
            {
                x->parent->color = black;
                y->color = black;
                x->parent->parent->color = red;
                x = x->parent->parent;
            }
            else
            {
                if (x == x->parent->left)
                {
                    x = x->parent;
                    rotate_right(x);
                }

                x->parent->color = black;
                x->parent->parent->color = red;
                ratate_left(x->parent->parent);
            }
        }
    }

    root()->color = black;  // Do not forget!
}

void RBTree::erase_rebalance(Node * z)
{
    Node * y = z;
    Node * x = nullptr;
    Node * x_parent = nullptr;

    if (y->left == nullptr)
        x = y->right;
    else if (y->right == nullptr)
        x = y->left;
    else
    {
        y = y->right;
        while (y->left)
            y = y->left;
        x = y->right;
    }  

    if (y != z)  // if y is z's successor
    {
        z->left->parent = y;
        y->left = z->left;
        if (y != z->right)
        {
            x_parent = y->parent;
            if (x)
                x->parent = y->parent;
            y->parent->left = x;
            y->right = z->right;
            z->right->parent = y;
        }
        else
            x_parent = y;

        if (root() == z)
            root() = y;
        else if (z->parent->left == z)
            z->parent->left = y;
        else
            z->parent->right = y;
        y->parent = z->parent;
        swap(y->color, z->color);
        y = z;
    }
    else
    {
        x_parent = y->parent;
        if (x)
            x->parent = y->parent;
        if (root() == z)
            root() = x;
        else if (z->parent->left == z)
            z->parent->left = x;
        else
            z->parent->right = x;
    }
    // now, y is pointing to what you will erase!
    //      x is the child of y, and note that x might be nullptr.



    // Now, the actual reblance is coming!
    // .....
    if (y->color == black)
    {
        while (x != root() && (x == nullptr || x->color == black))
        {
            if (x == x_parent->left)
            {
                Node * w = x_parent->right;
                if (w->color == red)                                      // Case 1
                {
                    w->color = black;
                    x_parent->color = red;
                    ratate_left(x_parent);
                    w = x_parent->right;
                }

                if ((w->left == nullptr || w->left->color == black) &&    // Case 2
                    (w->right == nullptr || w->right->color == black))
                {
                    w->color = red;
                    x = x_parent;
                    x_parent = x_parent->parent;
                }
                else
                {
                    if (w->right == nullptr || w->right->color == black)  //Case 3
                    {
                        if (w->left)
                            w->left->color = black;
                        w->color = red;
                        rotate_right(w);
                        w = x_parent->right;
                    }

                    w->color = x_parent->color;                           // Case 4
                    x_parent->color = black;
                    if (w->right)
                        w->right->color = black;
                    ratate_left(x_parent);
                    break;
                }
            }
            else  // same as above, just left <-> right
            {
                Node * w = x_parent->left;
                if (w->color == red)
                {
                    w->color = black;
                    x_parent->color = red;
                    rotate_right(x_parent);
                    w = x_parent->left;
                }

                if ((w->right == nullptr || w->right->color == black) &&
                    (w->left == nullptr || w->left->color == black))
                {
                    w->color = red;
                    x = x_parent;
                    x_parent = x_parent->parent;
                }
                else
                {
                    if (w->left == nullptr || w->left->color == black)
                    {
                        if (w->right)
                            w->right->color = black;
                        w->color = red;
                        ratate_left(w);
                        w = x_parent->left;
                    }

                    w->color = x_parent->color;
                    x_parent->color = black;
                    if (w->left)
                        w->left->color = black;
                    rotate_right(x_parent);
                    break;
                }
            }
        }

        if (x)
            x->color = black;
    }
}

void RBTree::in_order(Node * node)
{
    if (node == nullptr)
        return;

    in_order(node->left);
    cout << "( " << node->key << ", " << node->color << " )" << endl;
    in_order(node->right);
}

RBTree::RBTree()
{
    header = new Node(0);
}

RBTree::~RBTree()
{
    destroy(root());
    delete header;
    header = nullptr;
}

Node * RBTree::insert(int key)
{
    Node * cur = root();
    Node * pre = header;
    while (cur)
    {
        pre = cur;
        if (key < cur->key)
            cur = cur->left;
        else if (key > cur->key)
            cur = cur->right;
        else
            return nullptr;
    }

    cur = new Node(key);
    cur->parent = pre;
    if (pre == header || key < pre->key)
        pre->left = cur;
    else
        pre->right = cur;

    insert_rebalance(cur);
    return cur;
}

Node * RBTree::find(int key)
{
    Node * z = root();
    while (z)
    {
        if (key < z->key)
            z = z->left;
        else if (key > z->key)
            z = z->right;
        else
            return z;
    }
    return z;
}

void RBTree::erase(int key)
{
    Node * z = find(key);
    if (z)
    {
        erase_rebalance(z);
        delete z;
    }
}

void RBTree::print()
{
    in_order(root());
    cout << endl;
}

int main()
{
    RBTree rb_tree;

    // test "insert"
    rb_tree.insert(7);
    rb_tree.insert(2);
    rb_tree.insert(1); rb_tree.insert(1);
    rb_tree.insert(5);
    rb_tree.insert(3);
    rb_tree.insert(6);
    rb_tree.insert(4);
    rb_tree.insert(9);
    rb_tree.insert(8);
    rb_tree.insert(11); rb_tree.insert(11);
    rb_tree.insert(10);
    rb_tree.insert(12);
    rb_tree.print();

    // test "find"
    Node * p = nullptr;
    cout << ((p = rb_tree.find(2)) ? p->key : -1) << endl;
    cout << ((p = rb_tree.find(100)) ? p->key : -1) << endl << endl;

    // test "erase"
    rb_tree.erase(1);
    rb_tree.print();
    rb_tree.erase(9);
    rb_tree.print();
    rb_tree.erase(11);
    rb_tree.print();

    return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值