Cpp || 红黑树

红黑树

一:红黑树简介
1.概念
  • 红黑树

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

在这里插入图片描述

2.红黑树的性质

相比于平衡二叉树,去掉了平衡因子这种保持平衡的方式,而引入了新的机制(用树中结点的颜色来维持平衡).

  • 1.每个结点不是红色就是黑色

  • 2.根节点都是黑色的

  • 3.如果一个结点是红色的,则它的两个孩子结点是黑色,如果一个结点时黑色的,那么它的子结点没有规律

  • 4.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

  • 5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

  • 根据规则4:新增的结点必须是红色的

  • 根据规则3:新增的结点之父结点必须是黑色的

  • 当新的结点根据二叉树的规则到达其插入点,却未符合上述条件时,就必须调整颜色并旋转树形

注意:叶子结点不一定为红,可以为黑(那是调整过后的结果)

3.红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为根节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中值最小的节点,_pRight域指向红黑树中值最大的节点

  • 红黑树示意图

在这里插入图片描述

  • 结点结构体的定义
enum Color{
  Red, 
  Black
  };
   //用枚举的方式标识结点的颜色
   //用0代表红色,用1代表黑色
  template <class T>
  struct RBTNode{  
  RBTNode(const T& kv=T(),Color color=Red)
      :_kv(kv)    
       ,_color(color)    
        ,_left(nullptr)     
        ,_right(nullptr)   
        ,_parent(nullptr) 
        {  
        }  
        T _kv; 
         Color _color;  
         RBTNode<T>* _left; 
         RBTNode<T>* _right;  
         RBTNode<T>* _parent;
       };
二:红黑树的插入
  • 1.按照二叉搜索树的规则插入新的结点

  • 2.检测新结点插入完成之后,红黑树的性质是否遭到破坏

  • 因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;

  • 但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前插入的结点,p为父结点,g为祖父结点,u为叔叔结点

情况1:cur为红,p为红,g为黑,u存在且为红
  • 图示

在这里插入图片描述

  • 解决办法

将p,u改为黑,g改为红,然后g 把当成cur,继续向上调整

情况2:cur为红,p为红,g为黑,u不存在/u为黑
1.图示(u不存在)

在这里插入图片描述

  • 解决办法
  • 以g为轴进行右单旋,并将g变红p变黑
2.图示(u存在且为黑色,p为g的左孩子,cur为p的左孩子)

在这里插入图片描述

  • 解决办法
  • p为g的左孩子,cur为p的左孩子,则进行右单旋转;
  • p、g变色–p变黑,g变红
3.图示(p为g的右孩子,cur为p的右孩子)

在这里插入图片描述

  • 解决办法:
  • 相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转
  • p、g变色–p变黑,g变红
情况3:cur为红,p为红,g为黑,u不存在/u为黑
1.图示(p为g的左孩子,cur为p的右孩子)

在这里插入图片描述

  • p为g的左孩子,cur为p的右孩子,则针对p做左单旋转,再针对g做右单旋,并g变红,p变黑.此时的cur为图中树的根结点,但是cur如果不是_head->_parent则继续调整,若cur是_head->_parent则将cur变黑(红黑树的根结点颜色为黑),结束调整
2.图示(p为g的右孩子,cur为p的右孩子)

在这里插入图片描述

  • 相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,再针对g做左单旋,并g变红,p变黑.此时的cur为图中树的根结点,但是cur如果不是_head->_parent则继续调整,若cur是_head->_parent则将cur变黑(红黑树的根结点颜色为黑),结束调整
一个由上而下的程序
  • 为了避免"父子节点皆为红色"的情况持续向RB-tree的上层结构发展,形成处理时失效上的瓶颈,我们可以施行一个由上而下的程序(top-downprocedure):假设新增结点为A,那么就沿着A的路径,只要看到有某节点X的两个子节点为红色,就把X改为红色,并把两个子结点改为黑色,
  • 如果X的父节点P亦为红色(注意:此时S绝不可能为红),需要做一次单旋并改变颜色.
  • 此后,结点35的插入就很单纯了,要么直接插入,要么插入之后(若X结点为红)再一次旋转(单双皆可以)即可

在这里插入图片描述

三:红黑树的整体实现
#pragma  once
#include<iostream>
#include<functional>
using namespace std;
 
enum Color{
  Red,
  Black
};

template <class T>

struct RBTNode{
  RBTNode(const T& kv=T(),Color color=Red)
    :_kv(kv)
     ,_color(color)
     ,_left(nullptr)
     ,_right(nullptr)
     ,_parent(nullptr)
  {

  }
  T _kv;
  Color _color;
  RBTNode<T>* _left;
  RBTNode<T>* _right;
  RBTNode<T>* _parent;
};

template <class T>

class RBTree{
  public:
    typedef RBTNode<T> Node;
    typedef RBTNode<T>* pNode;

    RBTree()
      :_head(new Node)
    {
      _head->_left=_head;
      _head->_right=_head;
    }

    bool Insert(T& kv){
      if(_head->_parent==nullptr){
        //第一次插入的时候插入的是根结点,且颜色为黑色
        pNode _root=new Node;
        _root->_kv=kv;
        _root->_color=Black;
        _root->_parent=_head;
        _head->_left=_root;
        _head->_right=_root;
        _head->_parent=_root;
        return true;
      }

      //处理存在根结点的情况
      pNode cur=_head->_parent;
      pNode parent=nullptr;
      while(cur){
        parent=cur;
        if(cur->_kv > kv){
          cur=cur->_left;
        }else if(cur->_kv < kv){
          cur=cur->_right;
        }else{
          return false;
        }
      }
      
      //进行插入
      cur=new Node;
      cur->_kv=kv;

      if(parent->_kv>cur->_kv){
        parent->_left=cur;
      }else{
        parent->_right=cur;
      }

      cur->_parent=parent;

      //进行调整变色
      while(cur!=_head->_parent && cur->_parent->_color==Red){
        pNode parent=cur->_parent;
        pNode gparent=parent->_parent;
        if(gparent->_left==parent){
          pNode uncle=gparent->_right;
          //u存在且为红
          if(uncle && uncle->_color==Red){
            //情况一
            parent->_color=Black;
            uncle->_color=Black;
            gparent->_color=Red;
            cur=gparent;
          }else{
            //u不存在 / 存在且为黑
            //1.以g为轴右旋
            //2.调整颜色,p为黑色,g为红色

            //检查是否为双旋场景:左右双旋
            if(parent->_right==cur){
              RotateL(parent);
              swap(parent,cur);
            }
            RotateR(gparent);
            parent->_color=Black;
            gparent->_color=Red;
            break;
          }
        }else{
          pNode uncle=gparent->_left;
          if(uncle && uncle->_color==Red){
            parent->_color=uncle->_color=Black;
            gparent->_color=Red;
            cur=gparent;

          }else{
            //u存在且为黑/u不存在
            if(parent->_left==cur){
              RotateR(parent);
              swap(cur,parent);
            }

            RotateL(gparent);
            gparent->_color=Red;
            parent->_color=Black;
            break;
          }
        }
      
      }
      //红黑树根始终是黑色的
      _head->_parent->_color=Black;
      //为了实现后续的迭代器,将head的左右指针指向最大,最小的值
      _head->_left=leftMost();
      _head->_right=rightMost();
      return true;

    }

    pNode leftMost(){
      pNode cur=_head->_parent;
      while(cur && cur->_left){
        cur=cur->_left;
      }
      return cur;
    }

    pNode rightMost(){
      pNode cur=_head->_parent;
      while(cur && cur->_right){
        cur=cur->_right;
      }
      return cur;
    }

    void RotateL(pNode parent){
      pNode subR=parent->_right;
      pNode subRL=subR->_left;

      subR->_left=parent;
      parent->_right=subRL;

      if(subRL){
        subRL->_parent=parent;
      }

      if(parent!=_head->_parent){
        pNode gparent=parent->_parent;

        //判断parent之前是parent->_parent 的那一边的结点
        //把subR链接到对应的边
        if(gparent->_left==parent)
          gparent->_left=subR;
        else 
          gparent->_right=subR;
        //更新subR的parent
        subR->_parent=gparent;
      }
      else {
        //如果parent是根,subR变成新的根
        subR->_parent=nullptr;
        _head->_parent=subR;
      }

      //链接subR与parent
      parent->_parent=subR;      
    }

   void RotateR(pNode parent){
     pNode subL=parent->_left;
     pNode subLR=subL->_right;

     //1.单向链接subL,subLR,parent
     subL->_right=parent;
     parent->_left=subLR; 
     //2.向上链接subLR,parent 
     if(subLR){
       subLR->_parent=parent;
     }
     //3.双向链接subL与parent<_parent 
     if(parent!=_head->_parent){
       pNode gParent=parent->_parent;

       if(gParent->_left==parent){
         gParent->_left=subL;
       }else{
         gParent->_right=subL; 
       }

       //把subL的父亲结点改成其祖宗结点
       subL->_parent=gParent;
     }else{
       subL->_parent=nullptr;
       _head->_parent=subL; 
     }
     //4.向上链接parent,subL 
     parent->_parent=subL;


   }


    void Inorder(){
      _Inorder(_head->_parent);
      cout<<endl;
    }

    bool IsValidRBTree()
    {
      pNode pRoot = GetRoot();

      // 空树也是红黑树
      if (nullptr == pRoot)
        return true;

        // 检测根节点是否满足情况
        if (Black != pRoot->_color)
        {
          cout << "违反红黑树性质一:根节点必须为黑色!" << endl;
          return false;
        }
      // 获取任意一条路径中黑色节点的个数
      size_t blackCount = 0;
      pNode pCur = pRoot;
      while (pCur)
      {
        if (Black == pCur->_color)
          blackCount++;
        pCur = pCur->_left;
      }
      // 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
      size_t k = 0;
      return _IsValidRBTree(pRoot, k, blackCount);
    }

  private:
    void _Inorder(pNode parent){
      if(parent){
        _Inorder(parent->_left);
        cout<<parent->_kv<<" ";
        _Inorder(parent->_right);
      }
    }

    pNode GetRoot(){
      return _head->_parent;
    }

    
    bool _IsValidRBTree(pNode pRoot, size_t k, const size_t blackCount) {
      //走到null之后,判断k和black是否相等
      if (nullptr == pRoot)
      {
        if (k != blackCount)
        {
          cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
          return false;
        }
        return true;
      }

      // 统计黑色节点的个数
      if (Black == pRoot->_color)
        k++;
      // 检测当前节点与其双亲是否都为红色
      pNode pParent = pRoot->_parent;
      if (pParent && Red == pParent->_color && Red == pRoot->_color)
      {
        cout << "违反性质三:没有连在一起的红色节点" << endl;
        return false;
      }
      return _IsValidRBTree(pRoot->_left, k, blackCount) &&
        _IsValidRBTree(pRoot->_right, k, blackCount);
    }

  private:
    pNode _head;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值