当前位置: 首页 > news >正文

【数据结构】红黑树原理及实现

目录

  • 一. 红黑树的概念
    • 1. 红黑树的规则
      • 思考
    • 2. 红黑树的效率
  • 二.红黑树的实现
    • 1. 红黑树的结构
    • 2. 红黑树的插入
    • 3. 红黑树的平衡调整
      • 情况1:变色
      • 情况2:单旋+变色
      • 情况3:双旋+变色
    • 4. 红黑树插入及平衡调整代码实现
    • 5.红黑树的验证

一. 红黑树的概念

二叉树的优化,每个节点有一个变量表示结点的颜色,可以是红色或黑色,通过对节点颜色的约束,可以确保没有路径比其他路径长出两倍,接近平衡。

1. 红黑树的规则

  1. 每个节点不是红色就是黑色
  2. 根节点是黑色
  3. 不能有相连的红色节点,也就是说一个节点如果是红色,他的孩子一定是黑色
  4. 对于任意一条从根节点到空的路径,黑色节点的数量都相同
    在这里插入图片描述

思考

为什么红黑树的最长路径不超过最短路径的两倍?
每条路径都有相同数量的黑色节点,假设一条路上全是黑色,这是最短的路径长度为h,由于不能有相连的红色节点,最长的路径是一个红一个黑的路径排列,长度为2h,所以对任意一条路径长度x来说,h<=x<=2h

2. 红黑树的效率

假设节点数量为N,h为最短路径长度,2h-1 <= N < 22*h-1, 分析可得h=logN,由此可见最快的查找为logN,最坏的情况下高度为2h,查找效率也还是2*logN,所以红黑树的查找效率为O(logN)

二.红黑树的实现

1. 红黑树的结构

用bool类型来定义树的颜色,
定义parent,left和right指针方便后续的调整

typedef bool RBTreeColor;
const  RBTreeColor RBTreeRed = true;
const  RBTreeColor RBTreeBlack = false;template <class K,class V>
struct RBTreeNode
{typedef RBTreeNode Node;pair<K, V> _kv;Node* _parent;Node* _left;Node* _right;RBTreeColor _color;RBTreeNode(const pair<K,V>& kv):_kv(kv),_parent(nullptr),_left(nullptr),_right(nullptr),_color(RBTreeRed){}
};
template <class K, class V>
class RBTree
{
public:typedef RBTreeNode Node;private:Node*  _root=nullptr;
};

2. 红黑树的插入

  1. 先按照二叉树的规则进行插入
  2. 如果是空树插入,则新插入的节点为黑色,成为树的根,否则新插入的节点为红色,这是为了维护每条路径上的黑色节点数量相同这个规则
  3. 非空树插入后,新增节点为红色,如果父节点为黑色,不违反插入规则,插入结束
  4. 非空树插入后,新增节点为红色,如果父节点也为红色,违反插入规则,需要进行平衡调整

3. 红黑树的平衡调整

走到了平衡这一步,一定是因为出现了相连的两个红色节点,新插入节点c(cur)为红色,插入节点的父节点p(parent)一定也为红色,父节点的父节点g(grandparent)一定是黑色,这三个节点的颜色都是固定的,关键看父节点兄弟节点u(uncle)的情况,根据u的情况,可以分出几种平衡调整规则

情况1:变色

u存在且为红,p和u变成黑色,g变成红色,把g当成新的c,继续向上更新
分析:以g为根的左右子树的黑色节点数是一定的,把g变成红色,p和u变成黑色,不会影响左右子树的黑色节点数,以g为根的树的黑色节点数的也没有改变,由于g的父节点也是红色,所以需要把g当成新的c节点继续向上更新
在这里插入图片描述

情况2:单旋+变色

u不存在或者存在且为黑
u不存在时,c一定是新增节点;u存在且为黑时,c一定不是新增节点,符合情况一
分析:这种情况必须使p的变成黑色,g变成红色,然后旋转,让p变成这棵树新的根,由于新的根是黑色,不管他的父节点是什么,都符合红黑树的规则,插入结束
关于旋转的具体分析参照AVL数章节
在这里插入图片描述
当p是g的左孩子,c是p的左孩子,以g为旋转点,进行右单旋,p变成新的根,

在这里插入图片描述
当p是g的右孩子,c是p的右孩子,以g为旋转点,进行左单旋,p变成新的根,
在这里插入图片描述
在这里插入图片描述

情况3:双旋+变色

u不存在或者存在且为黑
u不存在时,c一定是新增节点;u存在且为黑时,c一定不是新增节点,符合情况一
分析:和情况二类似,根据情况单旋变双旋
在这里插入图片描述
当p是g的左孩子,c是p的右孩子,先以p为旋转点,进行左单旋,然后以g为旋转点,进行右单旋,c变黑,g变红,c变成这棵树新的根

在这里插入图片描述当p是g的右孩子,c是p的左孩子,先以p为旋转点,进行右单旋,然后以g为旋转点,进行左单旋,c变黑,g变红,c变成这棵树新的根
在这里插入图片描述
在这里插入图片描述

4. 红黑树插入及平衡调整代码实现

旋转代码参考AVL章节—>AVL原理及实现

};
template <class K, class V>
bool RBTree < K,V>::Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_color = RBTreeBlack;return true;}Node* parent = nullptr; Node* cur = _root;//二叉树规则插入while (cur){ if (cur->_kv.first < kv.first) { parent = cur;   cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur;    cur = cur->_left; }else { return false; } }cur = new Node(kv);//判断新增节点使父节点左还是右if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;while (parent && parent->_color == RBTreeRed){Node* grandfather = parent->_parent;//  g//p   u//p为g的左代码实现if (parent == grandfather->_left){Node* uncle = grandfather->_right;//  u存在且为红,p和u变黑,g变红,改变cur和p的指向,继续向上变;if (uncle && uncle->_color == RBTreeRed){parent->_color = uncle->_color = RBTreeBlack;grandfather->_color = RBTreeRed;cur = grandfather;parent = grandfather->_parent;}// u不存在或存在且为黑else{//      g//    p    u//  c//单旋加变色,c为p的左,   p变成新的根,p变黑,g变红if (cur == parent->_left){RorateR(grandfather);parent->_color = RBTreeBlack;grandfather->_color = RBTreeRed;}//        g//    p         u//       c//双旋加变色,c为p的右,else{RorateLR(grandfather);cur->_color = RBTreeBlack;grandfather->_color = RBTreeRed;}}}else{ //这里是p为g的情}return true;}

5.红黑树的验证

这里需要回忆一下红黑树四条规则,只要满足了这四条规则,就符合红黑树的标准
在这里插入图片描述

  1. 节点颜色用bool值标记,天然满足这个规则
  2. 检查根节点是否是黑色即可
  3. 一个节点可能不止一个孩子,但一个节点只能有一个父亲,所以遇到一个红色节点就查他的父亲是否是黑色,满足这点就满足了第三点
  4. 可以先序计算任意一条路径黑色节点的数量 ,然后让每个路径的黑色节点数和他比较,如果全都相等就满足第四条规则
template <class K, class V>
bool RBTree < K, V>::check(Node* root, int count, const int blackNum)
{// 检查到空树就比较黑色节点数if (root == nullptr){if (count == blackNum)return true;elsereturn false;}//如果有连续的红色节点,返回false,// 这里的root不会是根节点,如果是根节点在IsBanlance函数就已经检测出并返回了,所以每一个红色节点的父节点都不会为空if (root->_color == RBTreeRed && root->_parent->_color == RBTreeRed)return false;//遇到黑色节点if (root->_color == RBTreeBlack)count++;return check(root->_left, count, blackNum) && check(root->_right, count, blackNum);
}
template <class K, class V>
bool RBTree < K, V>::IsBanlance(Node* root)
{if (root == nullptr)return true;if (root->_color == RBTreeRed)return false;Node* cur = root;int count = 0;while (cur){if (cur->_color == RBTreeBlack)count++;cur = cur->_left;}
}

相关文章:

  • 如何在 MinGW 和 Visual Studio (MSVC) 之间共享 DLL
  • 动态规划(1)(java)(面试题)三步问题
  • SAM 2 (Segment Anything ):图像与视频通用分割模型
  • Steam游戏服务器攻防全景解读——如何构建游戏级抗DDoS防御体系?
  • Android ioctl 第二个参数命令码以及BINDER_FREEZE示例
  • vue3项目中eslint.config.ts配置rules
  • 18.ArkUI Video的介绍和使用
  • ECharts 地图开发入门
  • HD Tune Pro v6.10 简体中文汉化单文件版
  • C++_数据结构_详解红黑树
  • Winform(1.Winform控件学习)
  • 每天学一个 Linux 命令(31):md5sum
  • Linux安全模块:SELinux与AppArmor深度解析
  • ✨ Apifox:这玩意儿是接口界的“瑞士军刀”吧![特殊字符][特殊字符]
  • XYNU2024信安杯-REVERSE(复现)
  • kafka与flume的整合、spark-streaming
  • 量子加密通信技术及其应用:构建无条件安全的通信网络
  • 【合新通信】浸没式液冷光模块与冷媒兼容性测试技术报告
  • 【滑动窗口+哈希表/数组记录】Leetcode 3. 无重复字符的最长子串
  • 搜索二叉树-key的搜索模型
  • 剪纸纹样“流动”在水乡,谁不忆江南
  • 释新闻|印度宣布“掐断”巴基斯坦水源,对两国意味着什么?
  • 2025年度人大立法工作计划将公布:研究启动法律清理工作
  • 国家市监总局:民生无小事,严打民生领域侵权假冒违法行为
  • 百台新车首秀上海车展,跨国车企联手中国技术开启智能化下半场
  • 贝壳:网传“深圳贝壳内部通知”不实