红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是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. python 国内镜像

    pipy国内镜像目前有: http://pypi.douban.com/  豆瓣 http://pypi.hustunique.com/  华中理工大学 http://pypi.sdutlinux.o ...

  2. TypeScript 函数 (五)

    传递给一个函数的参数个数必须与函数期望的参数个数一致. 参数类别: 必须参数 可选参数 :可选参数必须在参数后面. 默认参数 :当用户没有传递这个参数或传递的值是undefined时. 它们叫做有默认 ...

  3. hdu 5090 Game with Pearls(最大匹配)

    Game with Pearls Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  4. EditText ------- 键盘类型

    文本输入框指定软键盘类型和软键盘回车键图标设置, 转载:http://blog.csdn.net/wirelessqa/article/details/8567327

  5. xml html xhtml html5

    1.XML 什么是 XML? XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 X ...

  6. SQL Server系统函数简介[转]

    一.字符转换函数1.ASCII()返回字符表达式最左端字符的ASCII 码值.在ASCII()函数中,纯数字的字符串可不用‘’括起来,但含其它字符的字符串必须用‘’括起来使用,否则会出错.2.CHAR ...

  7. linux的%用法

    转自:http://blog.csdn.net/wu020708/article/details/52387473 linux (%和%%)(#和##)贪婪匹配规则 先看一个案例,提取文件名: fil ...

  8. 『浅入浅出』MySQL 和 InnoDB

    作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.PostgreSQL,但是一直以来也没有对数据库有一个非常清晰并且成体系 ...

  9. 巨蟒python全栈开发-第12天 生成器函数 各种推导式 yield from

    一.今日主要内容总览(重点) 1.生成器(目的:帮助我们创建对象) (1)生成器的本质就是迭代器 (2)一个一个的创建对象 (3)创建生成器的方式: 1.生成器函数 2.通过生成器表达式来获取生成器 ...

  10. 无NavigationBar到有NavigationBar视图切换时的一个坑

    NavigationController在iOS App中是最常见不过了,可以说是每个App中必备的了.自iOS7开始,系统自带的右滑返回效果,也可以让有NavigationBar的视图切换很丝滑流畅 ...