平衡搜索树--红黑树 RBTree
红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black。
通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。
红黑树是满足下面红黑性质的二叉搜索树:
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的左孩子

插入节点:
- 首先,要找到插入该节点的位置,找到后就插入节点
 - 然后,对红黑树的节点颜色的合法性进行检查,并根据检查结果进行变色或者旋转。
 
基于以上的情况,红黑树利用模板类封装的插入函数算法就完成了:
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树,所以实际运用 中红黑树更多。
平衡搜索树--红黑树 RBTree的更多相关文章
- 高级搜索树-红黑树(RBTree)解析
		
目录 红黑树的定义 节点与树的定义 旋转操作 插入操作 情况1:p的兄弟u为黑色 情况2: p的兄弟u为红色 插入操作性能分析 代码实现 删除操作 情况1:x的接替者succ为红色 情况2:x的接替者 ...
 - 高级搜索树-红黑树(RBTree)代码实现
		
代码实现 代码参考了<数据结构(c++语言版)>--清华大学邓俊辉 "RBTree.h" #pragma once //#include"pch.h" ...
 - java——红黑树 RBTree
		
对于完全随机的数据,普通的二分搜索树就很好用,只是在极端情况下会退化成链表. 对于查询较多的情况,avl树很好用. 红黑树牺牲了平衡性,但是它的统计性能更优(综合增删改查所有的操作). 红黑树java ...
 - 红黑树(RBTREE)之上-------构造红黑树
		
该怎么说呢,现在写代码的速度还是很快的,很高兴,o(^▽^)o. 光棍节到了,早上没忍住,手贱了一般,看到*D的优惠,买了个机械键盘,晚上就到了,敲着还是很舒服的,和老婆炫耀了一把哈哈. 光棍节再去* ...
 - 红黑树RBTree
		
#pragma onceenum colour //子节点的颜色{ RED, BLANK,};template<class K,class V>struct RBTree ...
 - 二叉查找树 平衡二叉查找树 红黑树   b树 b+树 链表 跳表 链表
		
https://www.cnblogs.com/mojxtang/p/10122587.html二叉树的新增遍历查找
 - 1.红黑树和自平衡二叉(查找)树区别 2.红黑树与B树的区别
		
1.红黑树和自平衡二叉(查找)树区别 1.红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单. 2.平衡 ...
 - 【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介
		
B 树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: ...
 - B树、B+树、红黑树、AVL树
		
定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2. ...
 
随机推荐
- mac 安装memcache扩展问题
			
执行php -i 报错: Warning: PHP Startup: memcached: Unable to initialize module Module compiled with build ...
 - drupal7使用数据库api db_query需要注意的地方
			
写自定义module时候需要从数据库检索数据,用到了数据库的api,用了下面的sql: $record = db_query("SELECT 'sampledate', 'time' FRO ...
 - Mybatis 二级缓存脏读
			
脏读的产生 Mybatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都有自己的二级缓存,不同的mapper的二级缓存互不影响.这样的设计一不注意就会引起脏读,从而导致数据一 ...
 - 利用python输出000至999中间的数
			
打造一个000-999的字典 ): : i = '+str(i) elif <=i and i<: i ='+str(i) else: i=str(i) with open('1.txt' ...
 - 【转】SpringMVC 拦截器
			
类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. 常用场景: 1.日志记录:记录请求信息的日志,以便进行信息监控.信息统计.计算PV(Page View)等. 2.权限 ...
 - pip安装Scrapy框架报错
			
安装: pip3 install scrapy==1.1.0rc3 一..解决scrapy安装错误: 二.具体操作: 1.在http://landinghub.visualstudio.com/vis ...
 - JavaScript 学习(3)核心对象
			
##JavaScript 学习 3 1.核心对象 1.1 String对象 声明和生成 var myString="Hello"; var myString=new String( ...
 - php accumulation rockmongo
			
php -r 'echo substr(sprintf("%o",fileperms("./")),-4);'
 - Casperjs中fill提交表单遇到的问题
			
1.if you access internet with proxy please add --ignore-ssl-errors=true --ssl-protocol=a ...
 - servlet 文件下载
			
[本文简介] 一个servlet 文件下载 的简单例子. [文件夹结构] [java代码] package com.zjm.www.servlet; import java.io.BufferedIn ...