平衡搜索树--红黑树 RBTree

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是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树,所以实际运用 中红黑树更多。

时间: 2024-11-02 19:54:26

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

java——红黑树 RBTree

对于完全随机的数据,普通的二分搜索树就很好用,只是在极端情况下会退化成链表. 对于查询较多的情况,avl树很好用. 红黑树牺牲了平衡性,但是它的统计性能更优(综合增删改查所有的操作). 红黑树java实现(不完整,没有进行删除节点的操作): (默认左倾红黑树) package RedBlackTree; //从任意一个节点到叶子节点,经过的黑色节点是一样的--红黑树是保持"黑平衡"的二叉树 //因为23树中的每一个节点到叶子节点的深度是相同的 //红黑树在严格意义上不是平衡二叉树,最大

红黑树 RBTree

概述:R-B Tree,又称为"红黑树".本文参考了<算法导论>中红黑树相关知识,加之自己的解,然后以图文的形式对红黑树进行说明.本文的主要内容包括:红黑树的特性,红黑树的时间复杂度和它的证明,红黑树的左旋.右旋.插入等操作. 1 R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为"红黑树",它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个

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

该怎么说呢,现在写代码的速度还是很快的,很高兴,o(^▽^)o. 光棍节到了,早上没忍住,手贱了一般,看到*D的优惠,买了个机械键盘,晚上就到了,敲着还是很舒服的,和老婆炫耀了一把哈哈. 光棍节再去*mall买个,带着上班用. 正题,构造红黑树,就是节点的插入与调整,具体的理论我就不说了,图我也不画了,别人画的很好,我在纸上画的我自己都不想看.   贴几个网址作为参考吧: 参考的文档:1.http://www.cnblogs.com/zhb-php/p/5504481.html (推荐) 2.h

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

B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中:否则,如果查询关键字比结点关键字小,就进入左儿子:如果比结点关键字大,就进入右儿子:如果左儿子或右儿子的指针为空,则报告找不到相应的关键字: 如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性

数据结构之——红黑树

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是red或black.通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡. 红黑树是满足下面红黑性质的二叉搜索树: 每个节点,不是红色就是黑色的 根节点是黑色的 如果一个节点是红色的,则它的两个子节点是黑色的(没有连续的红节点) 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点.(每条路径的黑色节点的数量相等) 这里分析一下为什么红黑树能保证

B树、B+树、红黑树、AVL树

定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2.除根结点以外的非叶子结点的儿子数为 M/2(向上取整) ~ M3.拥有 K 个孩子的非叶子节点包含 k-1 个keys(关键字),且递增排列4.所有叶子结点在同一层,即深度相同 (叶节点可以看成是一种外部节点,不包含任何关键字信息) 在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶

红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写

在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间的管理用到了AVL树 红黑树:平衡二叉树,广泛用在C++的STL中.map和set都是用红黑树实现的.我们熟悉的STL的map容器底层是RBtree,当然指的不是unordered_map,后者是hash. B/B+树用在磁盘文件组织 数据索引和数据库索引 Trie树 字典树,用在统计和排序大量字符

【算法导论】红黑树详解之一(插入)

本文地址:http://blog.csdn.net/cyp331203/article/details/42677833 作者:苦_咖啡 欢迎转载,但转载请注明出处,否则将追究相应责任,谢谢!. 红黑树是建立在二叉查找树的基础之上的,关于二叉查找树可以参看[算法导论]二叉搜索树的插入和删除和[算法导论]二叉树的前中后序非递归遍历实现.对于高度为h的二叉查找树而言,它的SEARCH.INSERT.DELETE.MINIMUM.MAXIMUM等操作的时间复杂度均为O(h).所以在二叉查找树的高度较高

AVL树、红黑树以及B树介绍

简介 首先,说一下在数据结构中为什么要引入树这种结构,在我们上篇文章中介绍的数组与链表中,可以发现,数组适合查询这种静态操作(O(1)),不合适删除与插入这种动态操作(O(n)),而链表则是适合删除与插入,而查询效率则就比较慢了,本文要分享学习的树就是为了平衡这种静态操作与动态操作的差距. 一.二叉查找树 简介 满足下面条件就是二叉查找树 任意节点左子树不为空,则左子树的值均小于根节点的值. 任意节点右子树不为空,则右子树的值均大于于根节点的值. 任意节点的左右子树也分别是二叉查找树. 没有键值