红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是RedBlack

通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。

红黑树是满足下面红黑性质的二叉搜索树:

1. 每个节点,不是红色就是黑色的

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点)

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

思考:为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍?

  最短的路径上节点的颜色全部都为黑色;最长的路径则为黑红交叉的路径,其上有与最短路径的黑节点数目相同的黑节点数和红节点数目。所以我们按照红黑树性质所建立的红黑树的最长路径必然不会超过最短路径的两倍!

建立红黑树的节点类:

插入的新节点默认是红色的。原因是:插入黑节点必然会影响所有路径都含有相同数目的黑色节点这一原则,较难维护!

enum Color
{
RED,
BLACK
}; template<class K,class V>
struct RBTreeNode
{
K _key;
V _value;
Color _color; //颜色
RBTreeNode<K, V>* _left; //指向左孩子的指针
RBTreeNode<K, V>* _right; //指向右孩子的指针
RBTreeNode<K, V>* _parent; //指向父节点的指针 RBTreeNode(const K& key=K(), const V&value=V())
:_key(key)
, _value(value)
, _color(RED)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};

  红黑树需要变色或利用旋转来降低高度的几种情况:

图注:g代表grandfather祖父节点;p代表parent父亲结点;u代表uncle叔叔节点;cur代表当前节点

一、父节点是祖父节点的左孩子

1.uncle的颜色是红色

①当前节点cur是parent的左孩子

②当前节点cur是parent的右孩子

 

2.uncle的颜色是黑色 或者 uncle为NULL

①cur是parent的左孩子,右单旋

②cur是parent的右孩子,先左后右双旋

二、父节点是祖父节点的右孩子

1.uncle的颜色是红色

①cur是parent的右孩子

②cur是parent的左孩子

2.uncle的颜色是黑色 或者 uncle为NULL

①cur是parent的右孩子

②cur是parent的左孩子

插入节点:

  1. 首先,要找到插入该节点的位置,找到后就插入节点
  2. 然后,对红黑树的节点颜色的合法性进行检查,并根据检查结果进行变色或者旋转。

基于以上的情况,红黑树利用模板类封装的插入函数算法就完成了:

template<class K, class V>
bool RBTree<K, V>::Insert(const K& key, const V& value)
{
if (_root == NULL)
{
_root = new RBTreeNode<K, V>(key, value);
_root->_color = BLACK;
return true;
}
// 找位置
RBTreeNode<K, V>* cur = _root;
RBTreeNode<K, V>* parent = NULL;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//插入
cur = new RBTreeNode<K, V>(key, value);
cur->_parent = parent;
if (parent->_key > key)
parent->_left = cur;
else if (parent->_key < key)
parent->_right = cur; //检查颜色分配是否满足要求
while (parent&&parent->_color==RED)
{
RBTreeNode<K, V>* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
RBTreeNode<K, V>* uncle = grandfather->_right;
if (uncle&&uncle->_color == RED)
{ //第一种情况 变色
grandfather->_color = RED;
parent->_color =BLACK;
uncle->_color = BLACK; cur = grandfather;
parent = grandfather->_parent;
}
else if( (uncle&&uncle->_color==BLACK)||uncle==NULL)
{
if (cur == parent->_left)
{//第二种情况 右单旋 cur必然有黑色孩子
parent->_color = BLACK;
grandfather->_color = RED;
RotateR(grandfather);
}
else
{//第三种情况 左右双旋
RotateL(parent);
parent->_color = BLACK;
grandfather->_color = RED;
RotateR(grandfather);
}
break;
}
}
else if (parent == grandfather->_right)
{
RBTreeNode<K, V>* uncle = grandfather->_left;
if (uncle&&uncle->_color == RED)
{//第一种情况 变色
grandfather->_color = RED;
parent->_color = BLACK;
uncle->_color = BLACK; cur = grandfather;
parent = cur->_parent;
}
else if( (uncle&&uncle->_color == BLACK)||uncle==NULL)
{//第二种情况 左单旋 cur必然有黑孩子
if (cur == parent->_right)
{
parent->_color = BLACK;
grandfather->_color = RED;
RotateL(grandfather);
}
else if (cur==parent->_left)
{//第三种情况 右左双旋
RotateR(parent);
parent->_color = BLACK;
grandfather->_color = RED;
RotateL(grandfather);
}
break;
}
}
}
_root->_color = BLACK;
return true;
}

  插入完成之后,我们无法直观的看出红黑树的节点颜色是否合法,也无法直观的看出每条路径的黑色节点数目是否相同。

所以,这里实现两个函数,方便检验红黑树的合法性。

  • 红黑树每条路径的黑色节点数目都相同,所以随意遍历一条路径,计算这条路上的黑色节点的数目。以该数据为标杆,和其他路径的黑色节点数目作比较,判断是否都相同。
  • 如果当前节点是红颜色并且它有父节点,那么再判断父节点的颜色是否也是红色,这样就能判断该树是否满足连续两个节点不能同时为红色这一性质。
//检验红黑树的合法性
template<class K, class V>
bool RBTree<K, V>::Check()
{
//统计红黑树每条路径上黑色节点的数量
int blackNum = 0;
RBTreeNode<K, V>* cur = _root;
while (cur)
{
if (cur->_color == BLACK)
blackNum++;
cur = cur->_left;
}
int CBNum = 0;
return _Check(_root,blackNum,CBNum);
} ////////////////// 递归辅助
template<class K, class V>
bool RBTree<K, V>::_Check(RBTreeNode<K, V>* root, int blackNum, int CBNum)
{
if (root == NULL)
return true;
if (root->_color == BLACK)
{
CBNum++;
if (root->_left == NULL&&root->_right == NULL)
{ //走到了叶子节点 将该条路径上的黑色节点数量与之前统计的黑色节点数量做比较
if (blackNum == CBNum)
{
return true;
}
else
{
cout << "叶子节点为" << root->_key << "路径的黑色节点数目与最左侧支路的黑色节点数目不相等 !" << endl;
return false;
}
}
}
else if (root->_parent&&root->_parent->_color == RED)
{//判断是否存在连续的两个红色节点
cout << root->_parent->_key << " 和 " << root->_key << " 为两个连续的红色节点" << endl;
return false;
}
//递归检验子路
return _Check(root->_left, blackNum, CBNum) && _Check(root->_right, blackNum, CBNum);
}

  红黑树和AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N)) 红黑树的不追求完全平衡,保证最长路径不超过最短路径的2倍,相对而言,降低了旋转的要求,所以性能会优于AVL树,所以实际运用 中红黑树更多。

github红黑树代码链接

平衡搜索树--红黑树 RBTree的更多相关文章

  1. 高级搜索树-红黑树(RBTree)解析

    目录 红黑树的定义 节点与树的定义 旋转操作 插入操作 情况1:p的兄弟u为黑色 情况2: p的兄弟u为红色 插入操作性能分析 代码实现 删除操作 情况1:x的接替者succ为红色 情况2:x的接替者 ...

  2. 高级搜索树-红黑树(RBTree)代码实现

    代码实现 代码参考了<数据结构(c++语言版)>--清华大学邓俊辉 "RBTree.h" #pragma once //#include"pch.h" ...

  3. java——红黑树 RBTree

    对于完全随机的数据,普通的二分搜索树就很好用,只是在极端情况下会退化成链表. 对于查询较多的情况,avl树很好用. 红黑树牺牲了平衡性,但是它的统计性能更优(综合增删改查所有的操作). 红黑树java ...

  4. 红黑树(RBTREE)之上-------构造红黑树

    该怎么说呢,现在写代码的速度还是很快的,很高兴,o(^▽^)o. 光棍节到了,早上没忍住,手贱了一般,看到*D的优惠,买了个机械键盘,晚上就到了,敲着还是很舒服的,和老婆炫耀了一把哈哈. 光棍节再去* ...

  5. 红黑树RBTree

    #pragma onceenum colour    //子节点的颜色{    RED,    BLANK,};template<class K,class V>struct RBTree ...

  6. 二叉查找树 平衡二叉查找树 红黑树 b树 b+树 链表 跳表 链表

    https://www.cnblogs.com/mojxtang/p/10122587.html二叉树的新增遍历查找

  7. 1.红黑树和自平衡二叉(查找)树区别 2.红黑树与B树的区别

    1.红黑树和自平衡二叉(查找)树区别 1.红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单. 2.平衡 ...

  8. 【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介

    B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: ...

  9. B树、B+树、红黑树、AVL树

    定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2. ...

随机推荐

  1. WPF实用知识点

    1.一个基本的WPF程序, 需要引入的程序集WindowsBase, PresentationCore, PresentationFramework using System; using Syste ...

  2. Volley 源码分析

    Volley 源码分析 图片分析 要说源码分析,我们得先看一下官方的配图: 从这张图中我们可以了解到 volley 工作流程: 1.请求加入优先队列 2.从缓存调度器中查看是否存在该请求,如果有(没有 ...

  3. 《ASP.NET 1200例》ref关键字与out关键字

    REF关键字 ref 关键字会导致通过引用传递的参数,而不是值. 通过引用传递的效果是在方法中对参数的任何改变都会反映在调用方的基础参数中. 引用参数的值与基础参数变量的值始终是一样的. 不要将“通过 ...

  4. Servlet及相关类和接口

    上一篇介绍了在Web项目中web.xml文件的配置信息,本篇主要介绍里面非常重要的配置——Servlet配置,重点介绍与Servlet相关的几个接口和类,包括Servlet接口.ServletConf ...

  5. CentOS 安装 dotnetcore

    参考官方教程:https://www.microsoft.com/net/core#linuxcentos 安装.NET CORE SDK sudo yum install libunwind lib ...

  6. Null value was assigned to a property of primitive type setter

    org.springframework.orm.jpa.JpaSystemException: Null value was assigned to a property of primitive t ...

  7. 修改docker时区

    在实际业务场景中,经常碰到启动了一个容器,容器的时区是UTC的导致还需要重新运行: 我们在具体处理时也出现了该显现 业务场景: 数据库系统定时备份脚本, 定时备份脚本按照每天备份, 通过k8s启动容器 ...

  8. delphi----Tstringlist,将有符号的数据变成数组"aaa,bbb,ccc"---->list[0]=aaa,list[1]=bbb

    //TStringList 常用方法与属性: var   List: TStringList;   i: Integer; begin   List := TStringList.Create;   ...

  9. Storm-源码分析-Topology Submit-Executor

    在worker中通过executor/mk-executor worker e, 创建每个executor (defn mk-executor [worker executor-id] (let [e ...

  10. <2014 05 21> 互联网时代的C语言——Go

    Go希望成为互联网时代的C语言. 多数系统级语言(包括Java和C#)的根本编程哲学来源于C++,将C++的面向对象进一步发扬光大.但是Go语言的设计者却有不同的看法,他们认为C++ 真的没啥好学的, ...