zoukankan      html  css  js  c++  java
  • 树:红黑树

    红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。

    红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。

    它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

    先看一颗典型的红黑树:

    除了符合二叉查找树基本特性外,红黑树还具有下列性质:

    1.每一个节点或者着成红色,或者着成黑色。

    2.根是黑色的。

    3.如果一个节点是红色的,那么它的子节点必须是黑色的。

    4.从一个节点到一个NULL指针的每一条路径必须包含相同数目的黑色节点。

    5.所有NULL结点称为叶子节点,且认为颜色为黑。

    正是因为这些规则限制,才保证了红黑树的自平衡。红黑树从根到叶子的最长路径不会超过最短路径的2倍。
    当插入或删除节点的时候,红黑树的规则有可能被打破。这时候就需要做出一些调整,来继续维持我们的规则。

    调整有两种方法:变色和旋转。
    而旋转又分成两种形式:左旋转和右旋转。

    变色:为了重新符合红黑树的规则,尝试把红色节点变为黑色,或者把黑色节点变为红色。

    左旋转:逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。

    右旋转:顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。

    插入一个例子(对,就是插入)

     

    经历了如下步骤:

    变色 -> 左旋转 -> 变色 -> 右旋转 -> 变色

    这里有几个问题:

    为什么插入的节点着色为"红色",而不是黑色呢?

    因为每条路径必须包含相同的黑色节点,所以插入红色意味着我们需要处理的情况越少。

    当然,如果树是NULL,根据根节点为黑色的原则,我们就必须播放黑色了。

    至于删除,参考插入就好了。

    红黑树结构:

    enum RBTColor{RED, BLACK};
    
    template <class T>
    class RBTNode{
        public:
            RBTColor color;    // 颜色
            T key;            // 关键字(键值)
            RBTNode *left;    // 左孩子
            RBTNode *right;    // 右孩子
            RBTNode *parent; // 父结点
    };

    左转c++实现:

    /* 
     * 对红黑树的节点(x)进行左旋转
     *
     * 左旋示意图(对节点x进行左旋):
     *      px                              px
     *     /                               /
     *    x                               y                
     *   /        --(左旋)-->           /                 #
     *  lx   y                          x  ry     
     *     /                          /  
     *    ly   ry                     lx  ly  
     *
     *
     */
    template <class T>
    void RBTree<T>::leftRotate(RBTNode<T>* &root, RBTNode<T>* x)
    {
        // 设置x的右孩子为y
        RBTNode<T> *y = x->right;
    
        // 将 “y的左孩子” 设为 “x的右孩子”;
        // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
        x->right = y->left;
        if (y->left != NULL)
            y->left->parent = x;
    
        // 将 “x的父亲” 设为 “y的父亲”
        y->parent = x->parent;
    
        if (x->parent == NULL)
        {
            root = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
        }
        else
        {
            if (x->parent->left == x)
                x->parent->left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
            else
                x->parent->right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
        }
        
        // 将 “x” 设为 “y的左孩子”
        y->left = x;
        // 将 “x的父节点” 设为 “y”
        x->parent = y;
    }

    右转c++实现

    /* 
     * 对红黑树的节点(y)进行右旋转
     *
     * 右旋示意图(对节点y进行左旋):
     *            py                               py
     *           /                                /
     *          y                                x                  
     *         /        --(右旋)-->            /                       #
     *        x   ry                           lx   y  
     *       /                                    /                    #
     *      lx  rx                                rx  ry
     * 
     */
    template <class T>
    void RBTree<T>::rightRotate(RBTNode<T>* &root, RBTNode<T>* y)
    {
        // 设置x是当前节点的左孩子。
        RBTNode<T> *x = y->left;
    
        // 将 “x的右孩子” 设为 “y的左孩子”;
        // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
        y->left = x->right;
        if (x->right != NULL)
            x->right->parent = y;
    
        // 将 “y的父亲” 设为 “x的父亲”
        x->parent = y->parent;
    
        if (y->parent == NULL) 
        {
            root = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
        }
        else
        {
            if (y == y->parent->right)
                y->parent->right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
            else
                y->parent->left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
        }
    
        // 将 “y” 设为 “x的右孩子”
        x->right = y;
    
        // 将 “y的父节点” 设为 “x”
        y->parent = x;
    }

     

    红黑树

    漫画算法:什么是红黑树

    红黑树(四)之 C++的实现

  • 相关阅读:
    同台电脑 多Git账号同时使用
    netty对http协议解析原理解析(转载)
    Netty 线程模型与Reactor 模式
    增量/存量数据按时间维度分组
    网易技术分享:Nginx缓存引发的跨域惨案
    全面剖析Redis Cluster原理和应用
    聊聊阿里社招面试,谈谈“野生”Java程序员学习的道路
    美团点评基于 Flink 的实时数仓建设实践
    美团技术分享:大众点评App的短视频耗电量优化实战
    美团技术分享:美团深度学习系统的工程实践
  • 原文地址:https://www.cnblogs.com/losophy/p/9520250.html
Copyright © 2011-2022 走看看