C++之红黑树

9 篇文章 0 订阅

红黑树

1. 概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

2. 性质

  1. 每个结点不是红色就是黑色
  2. 根节点为黑色
  3. 如果节点为红,其子结点必须为黑
  4. 对于任一结点至NULL(树尾端)的任何路径,所含黑节点数必须相同
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

根据规则4,新增节点必须为红色;根据规则3,新增节点的父节点必须为黑。当新节点根据二叉搜索树的规则达到其插入点,却不符合上述条件,就必须调整颜色并旋转树形。

3. 插入节点

为了延续上图的状态,我们插入一些新节点,看看会发生什么变化,下面有四种不同的典型情况。

假设为图中的RB-tree分别插入3,8, 35, 75,根据二叉搜索树的规则,这四个新节点的落脚点如图所示,他们都破坏了RB-tree的规则,因此我们必须调整树形,也就是旋转树形并改变节点颜色。

  1. 如果U为红,将P和U变为黑色,G变为红色,如果GG节点(曾祖父)为黑,搞定,如果GG为红,就看情况4

  2. 如果U为红,将P和U变为黑色,G变为红色,如果GG仍为红,继续向上调整,直到不再有父子连续为红的情况

  3. 如果U为黑且C为外侧插入,对G进行单旋转,再将G变为红色,P变为黑色

  4. 如果U为黑且C为内侧插入,先对P和G进行单旋转,再将C变为黑色,G变为红色,再对G做一次单旋。

代码实现:

bool Insert(const pair<K, V>& kv) {
    if (_root == nullptr) {
        _root = new Node(kv);
        _root->_col = BLACK;
        return true;
    }

    // 不为空,开始插入,取出当前节点,寻找位置
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur) {
        if (cur->_kv.first > kv.first) { // 要插入的数据比该节点小,往左走
            parent = cur;
            cur = cur->_left;
        }
        else if (cur->_kv.first < kv.first) {// 要插入的数据比该节点大,往右走  
            parent = cur;
            cur = cur->_right;
        }
        else // 相同,返回失败
            return false;
    }
    // 走到这里说明找到了要插入的位置,为要插入的数据创建一个节点
    cur = new Node(kv);
    // 这时需要判断插入位置,与父节点的数据比较大小
    if (parent->_kv.first > kv.first)// 比父节点小,插入左边
        parent->_left = cur;
    else
        parent->_right = cur;
    // 将当前节点的_parent与父节链接
    cur->_parent = parent;

    // 更新颜色 -- 红色节点的左右子树只能为黑,我们默认插入的是红节点
    // cur:当前节点 parent:父节点 grandfather:祖父节点 uncle:叔叔节点
    while (parent && parent->_col == RED) {// 当插入节点的父节点也为红时,就需要调整了  
        // 情况一:cur红 parent红 grandfather黑 uncle存在且为红
        //     g
        //   p   u
        // c 
        Node* grandfather = parent->_parent;
        if (parent == grandfather->_left) {
            Node* uncle = grandfather->_right;
            if (uncle && uncle->_col == RED) {// uncle存在且为红
                grandfather->_col = RED;
                uncle->_col = BLACK;
                parent->_col = BLACK;
                //更新节点,继续向上,查看是否需要更新颜色
                cur = grandfather;
                parent = cur->_parent;
            }
            else {// uncle不存在/uncle为黑  
                //     g
                //   p   u
                // c 
                if (cur == parent->_left) {
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else {
                    //     g
                    //   p   u
                    //     c 
                    RotateL(parent);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                    RotateR(grandfather);
                }
                break;
            }
        }
        else { // (parent == grandfer->_right)
            //     g
            //   u   p
            //		   c 
            Node* uncle = grandfather->_left;
            if (uncle && uncle->_col == RED) { // uncle存在且为红
                grandfather->_col = RED;
                uncle->_col = BLACK;
                parent->_col = BLACK;

                // 更新节点,继续向上,查看是否需要更新颜色
                cur = grandfather;
                parent = cur->_parent;
            }
            else { // uncle不存在/uncle为黑
                //     g
                //   u   p
                //         c 
                if (cur == parent->_right) {
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else {
                    //     g
                    //   u   p
                    //     c 
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;
    return true;
}

4. 验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质

首先检测根节点的颜色,根节点为红色时,不是RB-tree,然后检测是否有连续的红色节点,一个节点,它的父节点为红色时,不是RB-tree

bool _Check(Node* root, int blackNum, int benchmark)
{
    if (root == nullptr)
    {
        if (benchmark != blackNum)
        {
            cout << "某条路径黑色节点的数量不相等" << endl;
            return false;
        }
        return true;
    }
    
    if (root->_col == BLACK)
        ++blackNum;
    
    if (root->_col == RED && root->_parent && root->_parent->_col == RED)
    {
        cout << "存在连续的红色节点" << endl;
        return false;
    }

    return _Check(root->_left, blackNum, benchmark) && _Check(root->_right, blackNum, benchmark);
}

bool IsBalance()
{
    if (_root && _root->_col == RED)
    {
        cout << "根节点的颜色是红色" << endl;
        return false;
    }
    
    int benchmark = 0;// 黑色节点的基准值
    Node* cur = _root;
    while (cur) // 遍历某一条路径计数黑色节点
    {
        if (cur->_col == BLACK)
            ++benchmark;
        cur = cur->_left;
    }

    return _Check(_root, 0, benchmark);
}

5. 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2 N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

6. 红黑树的应用

  1. C++ STL库 – map/set、mutil_map/mutil_set
  2. Java 库
  3. Linux内核
  4. 其他一些库
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值