红黑树浅析

1、红黑树概念:

红黑树是一种特殊的二叉搜索树,它在二叉搜索树的基础上对每一个节点进行着色,通过对着色的限制来保证树的近似平衡。红黑树的搜索最坏时间为O(logN),

2、红黑树性质:

①每个节点非黑即红

②根结点比为红

③每条路径的黑节点树相同(保证最长路径不会比最短路径长两倍)

④两个红节点不能连续

⑤每个叶子节点(空节点)为黑色

3、红黑树结构:

4、红黑树的插入步骤

①按照二叉搜索树的性质将节点插入树中正确的位置

②判断是否需要通过调节颜色/旋转使红黑树重新平衡

约定:pCur为新插入节点,p为父节点,u为叔叔节点,g为祖父节点

所有的插入情况及处理方式:

情况一:pCur为红,p为红色,g为黑色,u存在且为红色

策略:将p和u改为黑色,将g改为黑色,如果g还有双亲则继续向上调整

情况二:pCur为红,p为红色,g为黑色,u不存在/u为黑色

策略:p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反, p为g的右孩子,cur为p的右孩子,则进行左单旋转 p、g变色-->p变黑,g变红

情况三:cur为红,p为红,g为黑,u不存在/u为黑 

策略:p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反, p为g的右孩子,cur为p的左孩子,则针对p做右单旋转 则转换成了情况2

下面给出具体的插入实现代码:

#pragma once

#include <iostream>
using namespace std;

// 节点颜色
enum Color{RED, BLACK};

template <class T>
struct RBTreeNode{
  // 结点默认颜色给红色,因为需要保证每条路径黑色节点数相等
  RBTreeNode(const T& data = T(), Color color = RED)
    : _pLeft(nullptr)
    , _pRight(nullptr)
    , _pParent(nullptr)
    , _data(data)
    , _color(color)
  {}


  RBTreeNode<T>* _pLeft;
  RBTreeNode<T>* _pRight;
  RBTreeNode<T>* _pParent;
  T _data;
  Color _color;
};


template <class T>
class RBTree{
  typedef RBTreeNode<T> Node;

  RBTree(){
    _pHead->_pParent = nullptr;
    _pHead->_pLeft = _pHead;
    _pHead->_pRight = _pHead;
  }

  bool Insert(const T& data) {
    Node* pRoot = GetRoot();
    // 树为空则当前节点为根结点
    if(pRoot == nullptr) {
      pRoot = new Node(data, BLACK);

      // 将根和头结点连起来
      pRoot->_pParent = _pHead;
      _pHead->_pParent = pRoot;
      
    }

    // 树不为空,则按照二叉搜索树的性质插入节点
    // 找到插入位置
    Node* pCur = pRoot;
    Node* pParent = nullptr;
    while(pCur) {
      pParent = pCur;
      if(data > pCur->_data) {
        pCur = pCur->_pRight;
      } else if(data < pCur->_data) {
        pCur = pCur->_pLeft;
      } else{
        return false;
      }
    }
    // 插入元素
    pCur = new Node(data); 
    if(pParent->_data > data) {
      pParent->_pLeft = pCur;
      pCur->_pParent = pParent;
    } else {
      pParent->_pRight = pCur;
      pCur->_pParent = pParent;
    }

    // 如果双亲的颜色为红色则违反性质三,需要进行调整
    while(pParent && pParent->_color == RED) {
      Node* Grandfather = pParent->_pParent;   // 祖父节点一定存在且为黑色
      
      // 分左右讨论
      if(pParent == Grandfather->_pLeft) {

        // 叔叔节点存在且为红色
        Node* Uncle = Grandfather->_pRight;
        if(Uncle && Uncle->_color == RED) {
          pParent->_color = BLACK;
          Uncle->_color = BLACK;
          Grandfather->_color = RED;
          // Grandfather可能还有双亲,继续往上调整
          pCur = Grandfather;
          pParent = pCur->_pParent;
        } else {
          // 叔叔节点不存在或者为黑色
          if(pCur == pParent->_pRight) {
              // 需要先进行左旋
              RotateL(pParent);
              // 交换pCur和pParent
              swap(pCur, pParent);
          }
          // 将整棵树右旋
          RotateR(Grandfather);
          // 将pParent颜色改为黑色
          pParent->_color = BLACK;
          // 将Grandfather颜色改为红色
          Grandfather->_color = RED;
        }
      } else {
        // 考虑右边,右边与左边情况基本一样
        Node* Uncle = Grandfather->_pLeft;
        // 叔叔节点存在且颜色为红
        if(Uncle && Uncle->_color == RED) {
          pParent->_color = BLACK;
          Uncle->_color = BLACK;
          Grandfather->_color = RED;
          pCur = Grandfather;
          pParent = pCur->_pParent;
        } else {
          // 叔叔节点不存在或者为黑色
          if(pCur == pParent->_pLeft) {
            RotateR(pParent);
            swap(pCur, pParent);
          }
          RotateL(Grandfather);
          pParent->_color = BLACK;
          Grandfather->_color = RED;
        }
      }
    }

    // 调整完之后将根结点颜色设为黑色
    pRoot->_color = BLACK;
    return true;
  }


  // 验证红黑树
  // 首先检查是否满足二叉搜索树性质
  // 其次检查是否满足红黑树性质
  bool IsValidRBTree() {
    Node* pRoot = GetRoot();
    // 空树为红黑树
    if(pRoot == nullptr) {
      return true;
    }
    // 检测根结点是否为黑色
    if(pRoot->_color != BLACK) {
      cout << "红黑树根结点必须为黑色" << endl;
      return false;
    }

    // 先获取任意一条路径的黑色节点数作为参照
    int BlackCount = 0;
    Node* pCur = pRoot;
    while(pCur) {
      if(pCur->_color == BLACK)
        BlackCount++;
      pCur = pCur->_pLeft;
    }
    // 检验每条路径黑色节点数是否相等
    int K = 0;   // 记录每条路径的黑色节点个数
    _IsValidRBTree(pRoot, K, BlackCount);
  }
  private:

  bool _IsValidRBTree(Node* pRoot, int K, int BlackCount) {
    if(pRoot == nullptr) {
      if(K != BlackCount) {
        cout << "红黑树每条路径的黑色节点数必须相等" << endl;
        return false;
      }
      return true;
    }
    if(pRoot->_color == BLACK)
      K++;


    // 检验当前节点 && 其双亲颜色是否同为红色
    Node* pParent = pRoot->_pParent;
    if(pParent && pParent->_color == RED && pRoot->_color == RED) {
      cout << "红黑树不允许两个红色节点为父子关系" << endl;
      return false;

    }

    // 再去递归判断左右子树
    return _IsValidRBTree(pRoot->_pLeft, K, BlackCount) &&
          _IsValidRBTree(pRoot->_pRight, K, BlackCount);
  }

  // 左旋
  void RotateL(Node* pParent) {
    Node* pSubR = pParent->_pRight;
    Node* pSubRL = pSubR->_pLeft;

    // ①更新双亲的右孩子
    pParent->_pRight = pSubRL;
    // 判断pSubRL是否存在
    if(pSubRL) {
      // 更新其双亲
      pSubRL->_pParent = pParent;
    }
    // 更新pSubR左孩子
    pSubR->_pLeft = pParent;
    // 保存pParent的双亲
    Node* pPParent = pParent->_pParent;

    // 更新pParent和pSubR的双亲
    pParent->_pParent = pSubR;
    pSubR->_pParent = pPParent;
    
    // 如果pPParent为空,则pSub此时就为根结点
    if(pPParent == nullptr) {
      GetRoot() = pSubR;
      pSubR->_pParent = nullptr;

    } else {
      // 判断pParent是pPParent的左孩子还是右孩子
      if(pPParent->_pLeft == pParent) {
        pPParent->_pLeft = pSubR;
      } else {
        pPParent->_pRight = pSubR;
      }
    }
  }
  // 右旋
  void RotateR(Node* pParent) {
    Node* pSubL = pParent->_pLeft;
    Node* pSUbLR = pSubL->_pRight;

    pParent->_pLeft = pSUbLR;
    if(pSUbLR)
      pSUbLR->_pParent = pParent;

    // 更新左子树的右子树
    pSubL->_pRight = pParent;

    Node* pPParent = pParent->_pParent;
    pParent->_pParent = pSubL;
    if(pPParent == nullptr) {
      GetRoot() = pSubL;
      pSubL->_pParent = nullptr;

    } else {
      if(pPParent->_pLeft == pParent) {
        pPParent->_pLeft = pSubL;
      } else {
        pPParent->_pRight = pSubL;
      }
    }
  }
  Node* GetRoot() {
    return _pHead->_pParent;
  }
  private:
  Node* _pHead;  // 头结点

};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值