红黑树(有图解)

目录

介绍

概念

性质

模拟实现

结点定义 

插入 

保证平衡的原因

一般情况

特殊情况(uncle为黑) 

uncle不存在

旋转方式

右旋

迭代器

++ 

-- 

代码


介绍

概念

红黑树是一种自平衡的二叉搜索树

  • 它是在每个节点上引入额外的颜色信息,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,确保没有一条路径会比其他路径长出俩倍,从而达到高度差的平衡
  • 保证了在最坏情况下的时间复杂度为O(log n)
  • 同时,它也是c++标准库中两种关联容器(set和map)的底层实现
  • 红黑树(有图解)_第1张图片

性质

  • 结点颜色只有红色,黑色两种
  • 根结点必须是黑色
  • 每个叶子结点(也就是平常被我们忽略的空结点(NIL结点))都是黑色的
  • 红黑树(有图解)_第2张图片
  • 不能有两个连续的红色节点(从上到下的路径来看)
  • 从任意一个节点到其每个叶子节点的路径必须包含相同数目的黑色节点,这被称为黑色高度(Black Height)
满足以上性质后,可以保证红黑树中,其最长路径中节点个数不会超过最短路径节点个数的两倍

模拟实现

结点定义 

  • 和avl树一样,需要频繁用到父结点,所以需要一个parent成员
  • 除此之外,他还需要存储每个结点的颜色信息
  • 库中定义:
  • 红黑树(有图解)_第3张图片

  • stl库中,红黑树实际上还有一个哨兵位的头结点(当然没有也可以)
  • 红黑树(有图解)_第4张图片
  • 可以看到,树中定义了一个成员变量header
  • 且可以从命名来看,header的left指向树的最小结点,right指向树的最大结点,parent指向树的根结点
  • 红黑树(有图解)_第5张图片

其次,结点插入一般设置为红色

因为有不能有连续红色的性质,所以直接插入红色结点,有助于确保平衡

插入 

  • 首先,和avl树一样,需要先找到插入结点的位置(如果重复就不插入了)
  • 主要需要修改的是parent,uncle,grandfather的颜色,而cur(或是新插入结点)的位置确保是红色
  • !!!!注意!!!!一定要记住我们的红黑树是有一个头结点的
  • 记得一定要在旋转时  改根结点和头结点之间的连接
  • 以及判断循环的时候,有些结束条件就是遍历到头结点!!!

保证平衡的原因

  • 黑色结点数目相等:保证了每条路径的高度在可控范围内
  • 红色不能连续:保证红色节点的父节点和子节点之间的黑色节点数目是相等的,从而保持了黑色平衡性
  • 由于黑色结点数相等,虽然没有限制红色结点数,但不能有连续红色结点,这两个就保证红结点最多有黑结点个,最少没有,所以不会超过两倍

一般情况

红黑树(有图解)_第6张图片

需要让parent和uncle都变黑,而grandfather变红(注意:因为parent是红色,所以grandfather是一定存在的,注意性质嗷)

红黑树(有图解)_第7张图片

如果grandfather不是根结点,就需要继续向上调整,让cur指向grandfather的位置

如果是根结点,就结束

!!!!注意,循坏外一定要让根结点的颜色为黑,因为可能会调整根为红色

特殊情况(uncle为黑) 

红黑树(有图解)_第8张图片

一轮变色后,此时uncle为黑:

红黑树(有图解)_第9张图片

如果让parent,uncle变黑,grandfather变红,会让parent那条路径多一个黑色结点,而uncle那条路数目没有变,所以单纯的变色满足不了这种情况了

红黑树(有图解)_第10张图片

同理,uncle不存在也是一样 

uncle不存在

红黑树(有图解)_第11张图片

会发现,此时无法变色,否则会在parent分支中,多出一个黑色结点

而且,这个结构,是不是让我们想起了avl树中的右旋

所以,在这两种情况下,需要旋转+变色,才能保证红黑树的平衡

旋转方式

像上面这种情况,很明显是要对grandfather右旋,最后parent成为这支树的根结点

由此,旋转方式其实就是由cur,parent,grandfather的相对位置,来确定

右旋

像这个例子,就是非常典型的右旋

红黑树(有图解)_第12张图片

红黑树(有图解)_第13张图片

旋转后,让新的根结点为黑色,剩下两个为红色

红黑树(有图解)_第14张图片

这个变色方式确实很神奇

再来分析一下,上面的uncle为黑时的旋转方式

因为此时,这三个结点的相对位置依然是右旋

 红黑树(有图解)_第15张图片

右旋后:

红黑树(有图解)_第16张图片

然后进行变色,依然是根为黑色,其他两个为红色:

红黑树(有图解)_第17张图片

很神奇吧,这样就能让红黑树平衡了 

其它旋转方式都是一样的,旋转完后让根为黑色,其他两个为红色即可 

迭代器

底层实际上就是结点指针,在此之上进行了封装

++ 

还记得我们如果要遍历平衡树得用中序遍历吗,指针的挪动也是借助中序的思想

中序:左根右

  • 那么如果当前结点有右树,就找到右树中的最小结点
  • 如果当前结点没有右子树,那么就判断它和父亲的位置
  • 如果父亲的左子树就是当前结点,那么++后就是父亲
  • 如果是当前结点在右树,说明parent这一支子树已经遍历完毕,该从parent的位置继续判断
  • 直到parent遍历到了phead的位置(那个头结点)

-- 

和上面的++类似,只不过是倒着走

也就是右根左的方向

  • 那么如果当前结点有左树,就找到左树中的最大结点
  • 如果当前结点没有左子树,那么就判断它和父亲的位置
  • 如果父亲的右子树就是当前结点,那么--后就是父亲
  • 如果是当前结点在左树,说明parent这一支子树已经遍历完毕,该从parent的位置继续判断
  • 直到parent遍历到了phead的位置(那个头结点)

代码

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 有迭代器的红黑树
namespace my_RB_Tree
{
    enum colour
    {
        black,
        red
    };

    template 
    struct RBTreeNode // 结点
    {
        RBTreeNode(const T &data)
            : _left(nullptr),
              _right(nullptr),
              _parent(nullptr),
              _col(red),
              _data(data)
        {
        }
        RBTreeNode *_left;
        RBTreeNode *_right;
        RBTreeNode *_parent;
        colour _col;
        T _data;
    };

    template  // T是元素类型,ptr是指针类型,ref是引用类型(后两种会有const类型)
    struct RBTreeIterator                    // 迭代器
    {
        typedef RBTreeNode Node;
        typedef RBTreeIterator Self;

        RBTreeIterator(Node *pNode)
            : _pNode(pNode)
        {
        }

        // 让迭代器具有类似指针的行为
        Ref operator*()
        {
            return _pNode->_data;
        }
        Ptr operator->()
        {
            return &(_pNode->_data);
        }

        // 让迭代器可以移动:前置/后置++
        Self &operator++()
        {
            Increament();
            return *this;
        }
        Self operator++(int)
        {
            Self tmp(*this);
            Increament();
            return tmp;
        }
        // 让迭代器可以移动:前置/后置--
        Self &operator--()
        {
            DeIncreament();
            return *this;
        }
        Self operator--(int)
        {
            Self tmp(*this);
            DeIncreament();
            return tmp;
        }

        // 让迭代器可以比较
        bool operator!=(const Self &s) const
        {
            return _pNode != s._pNode;
        }
        bool operator==(const Self &s) const
        {
            return _pNode == s._pNode;
        }

    private:
        void Increament();
        void DeIncreament();

        Node *_pNode;
    };

    // 为了后序封装map和set,本代码的红黑树会有一个作为哨兵位的头结点
    template  // K是关键字的类型,T是元素类型(区分这两个的原因:会用该红黑树封装成set和map,而map是key_value的)
                                              // keyofT是返回关键字类型的值(否则map无法返回)
    class RBTree                              // 红黑树
    {
    public:
        typedef RBTreeNode Node;
        typedef RBTreeIterator iterator;
        typedef RBTreeIterator const_iterator;

    public:
        RBTree()
        {
            _pHead = new Node(T());
            _pHead->_left = _pHead;
            _pHead->_parent = nullptr;
            _pHead->_right = _pHead;
        }

        // 在红黑树中插入值为data的节点,插入成功返回true,否则返回false
        std::pair Insert(const T &data);
        

        // 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
        Node *Find(const K &data);

        // 获取红黑树最左侧节点
        Node *LeftMost();

        // 获取红黑树最右侧节点
        Node *RightMost();

        iterator begin()
        {
            return iterator(LeftMost());
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin() const
        {
            return const_iterator(LeftMost());
        }
        const_iterator end() const
        {
            return const_iterator(_pHead);
        }

        // 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
        bool IsValidRBTRee()
        {
            Node *root = _pHead->_parent;
            if (root->_col == red)
            {
                return false;
            }
            int count = 0;
            find_blacknode(count, _pHead->_parent);
            return _IsValidRBTRee(_pHead->_parent, count, 0);
        }

    private:
        bool _IsValidRBTRee(Node *pRoot, size_t blackCount, size_t pathBlack);
        // 左单旋
        void RotateL(Node *pParent);
        // 右单旋
        void RotateR(Node *pParent);
        // 为了操作树简单起见:获取根节点
        Node *&GetRoot()
        {
            return _pHead->_parent;
        }
        void find_blacknode(int &count, Node *root)
        {
            if (root == nullptr)
            {
                return;
            }
            if (root->_col == black)
            {
                ++count;
            }
            find_blacknode(count, root->_left);
            find_blacknode(count, root->_right);
        }

    private:
        Node *_pHead = nullptr;
    };

    template 
    void RBTree::RotateL(Node *pParent)
    {
        Node *cur = pParent->_right, *curleft = cur->_left;

        // 连接p和cur左树,因为该位置被p占据
        pParent->_right = curleft;
        if (curleft)
        {
            curleft->_parent = pParent;
        }

        // 连接父结点
        if (pParent->_parent != _pHead)
        {
            Node *ppnode = pParent->_parent;
            if (ppnode->_left == pParent)
            {
                ppnode->_left = cur;
            }
            else
            {
                ppnode->_right = cur;
            }
            cur->_parent = ppnode;
        }
        else
        {
            _pHead->_parent = cur;
            cur->_parent = _pHead;
        }
        // 连接p和cur
        pParent->_parent = cur;
        cur->_left = pParent;
    }
    template 
    void RBTree::RotateR(Node *pParent)
    {
        Node *cur = pParent->_left, *curright = cur->_right;

        // 连接p和cur右树,因为该位置被p占据
        pParent->_left = curright;
        if (curright)
        {
            curright->_parent = pParent;
        }

        // 连接父结点
        if (pParent->_parent != _pHead)
        {
            Node *ppnode = pParent->_parent;
            if (ppnode->_left == pParent)
            {
                ppnode->_left = cur;
            }
            else
            {
                ppnode->_right = cur;
            }
            cur->_parent = ppnode;
        }
        else
        {
            _pHead->_parent = cur;
            cur->_parent = _pHead;
        }
        // 连接p和cur
        pParent->_parent = cur;
        cur->_right = pParent;
    }

    template 
    typename RBTree::Node *RBTree::LeftMost()
    {
        Node *cur = _pHead->_parent;
        while (cur->_left)
        {
            cur = cur->_left;
        }
        return cur;
    }
    template 
    typename RBTree::Node *RBTree::RightMost()
    {
        Node *cur = _pHead->_parent;
        while (cur->_right)
        {
            cur = cur->_right;
        }
        return cur;
    }

    template 
    typename RBTree::Node *RBTree::Find(const K &data) // 注意这里,
    {
        Node *cur = _pHead->_parent;
        KeyOfT kot;
        while (cur)
        {
            if (data > kot(cur->_data))
            {
                cur = cur->_right;
            }
            else if (data < kot(cur->_data))
            {
                cur = cur->_left;
            }
            else
            {
                return cur;
            }
        }
        return nullptr;
    }

    template 
    std::pair::iterator, bool> RBTree::Insert(const T &data) // 为了和map适配,要返回pair类型
                                                                                                         //(first是插入元素所在的迭代器,second是bool值,判断是否成功插入)
    {
            KeyOfT kot;
            Node *newnode = nullptr;
            if (_pHead->_parent == nullptr)
            {
                newnode = new Node(data);
                newnode->_col = black;
                _pHead->_parent = newnode;
                newnode->_parent = _pHead;
                return std::make_pair(iterator(newnode), true);
            }
            else
            {
                Node *cur = _pHead->_parent, *parent = cur;
                while (cur)
                {
                    if (kot(data) > kot(cur->_data))
                    {
                        parent = cur;
                        cur = cur->_right;
                    }
                    else if (kot(data) < kot(cur->_data))
                    {
                        parent = cur;
                        cur = cur->_left;
                    }
                    else
                    {
                        return std::make_pair((iterator)cur, false);
                    }
                }

                newnode = new Node(data);
                cur = newnode;
                cur->_parent = parent;
                if (kot(parent->_data) > kot(cur->_data))
                {
                    parent->_left = cur;
                }
                else
                {
                    parent->_right = cur;
                }

                Node *grandfather = nullptr;
                while (parent && parent->_col == red)
                {
                    grandfather = parent->_parent; // 因为父结点是红色,所以肯定有爷爷结点(注意红黑树规则:根结点必须是黑色)

                    if (grandfather->_left == parent) // 确定父亲位置
                    {
                        Node *uncle = grandfather->_right; // 也就能确定叔叔位置

                        if (uncle && uncle->_col == red)
                        {
                            parent->_col = uncle->_col = black;
                            grandfather->_col = red;
                        }
                        else // 如果uncle不存在/为黑,就需要旋转+变色了
                        {
                            // 需要先判断旋转类型(也就是判断 -- parent和cur的相对位置)
                            if (parent->_left == cur)
                            {
                                // 一条偏右的直线,需要右旋
                                RotateR(grandfather);
                                // 旋转完后parent成为根结点

                                // 更改完结点指向后,就可以改颜色了(都是根结点为黑,另外两个为红)
                                parent->_col = black;
                                cur->_col = grandfather->_col = red; // 和cur一层
                            }
                            else
                            {
                                // 拐角在左边,也就是先左旋,再右旋
                                RotateL(parent);
                                RotateR(grandfather);
                                // cur成为根结点

                                // 改颜色
                                cur->_col = black;
                                parent->_col = grandfather->_col = red;
                            }
                            break;
                        }
                    }
                    else // parent在grandfather的右树
                    {
                        Node *uncle = grandfather->_left;

                        if (uncle && uncle->_col == red)
                        {
                            parent->_col = uncle->_col = black;
                            grandfather->_col = red;
                        }
                        else // 如果uncle不存在/为黑,就需要旋转+变色了
                        {
                            // 需要先判断旋转类型(也就是判断 -- parent和cur的相对位置)
                            if (parent->_right == cur)
                            {
                                // 一条偏左的直线,需要左旋
                                RotateL(grandfather);
                                parent->_col = black;
                                cur->_col = grandfather->_col = red; // 和cur一层
                            }
                            else
                            {
                                // 拐角在right,也就是先右旋,再左旋
                                RotateR(parent);
                                RotateL(grandfather);
                                // 改颜色
                                cur->_col = black;
                                parent->_col = grandfather->_col = red;
                            }
                            break;
                        }
                    }
                    cur = grandfather; // 注意,这里会改cur的指向,但返回值需要返回插入位置的迭代器,所以需要另外保存
                    parent = cur->_parent;
                }
                (_pHead->_parent)->_col = black; // 根结点必须为黑(防止它在上面的循环中被修改)
            }

            return std::make_pair(iterator(newnode), true);
        }

    template 
    bool RBTree::_IsValidRBTRee(Node *cur, size_t blackCount, size_t pathBlack)
    {
        if (cur == nullptr)
        {
            // 到空结点后,就说明一条路径已经走通了,可以用得到的黑色结点数与基准数对比,不一样就说明红黑树错误
            if (pathBlack != blackCount)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        if (cur->_parent)
        {
            Node *ppnode = cur->_parent;
            if (cur->_col == red && ppnode->_col == red)
            {
                return false;
            }
        }
        if (cur->_col == black)
        {
            ++pathBlack;
        }
        return _IsValidRBTRee(cur->_left, blackCount, pathBlack) && _IsValidRBTRee(cur->_right, blackCount, pathBlack);
    }

    template 
    void RBTreeIterator::Increament()
    {
        Node *cur = _pNode, *parent = _pNode->_parent;
        if (cur->_right)
        {
            // 找到右子树的最小结点
            Node *curright = cur->_right;
            while (curright->_left)
            {
                curright = curright->_left;
            }
            _pNode = curright;
        }
        else
        {
            while (parent->_parent != cur && parent->_right == cur) // 找到cur是parent的左结点的位置,这样parent的位置就是下一个位置
            {
                cur = parent;
                parent = parent->_parent;
            }
            _pNode = parent;
        }
    }
    template 
    void RBTreeIterator::DeIncreament()
    {
        Node *cur = _pNode, *parent = _pNode->_parent;
        if (cur->_left)
        {
            // 找到左子树的最大结点
            Node *curleft = cur->_left;
            while (curleft->_right)
            {
                curleft = curleft->_right;
            }
            _pNode = curleft;
        }
        else
        {
            while (parent->_parent != cur && parent->_left == cur) // 找到cur是parent的左结点的位置,这样parent的位置就是下一个位置
            {
                cur = parent;
                parent = parent->_parent; 
            }
            _pNode = parent;
        }
    }
}

你可能感兴趣的:(数据结构与算法,算法,数据结构,c++)