红黑树(RBTree)

这篇具有很好参考价值的文章主要介绍了红黑树(RBTree)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 红黑树的概念

红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为「对称二叉B树」,它现代的名字源于Leo J. Guibas和罗伯特·塞奇威克于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在O(log n)时间内完成查找、插入和删除,这里的n是树中元素的数目。红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:节点是红色或黑色。 根是黑色。 所有叶子都是黑色(叶子是NIL节点)。 每个红色节点必须有两个黑色的子节点。 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
在红黑树中,所有的叶子结点都是黑色的空节点(NIL节点)。NIL节点是红黑树额外引入的结点,在计算红黑数高度时NIL结点也会被计算在内。NIL结点指的是叶结点空的左右子结点延伸出来的的结点,也包括了叶子节点本身。
红黑树(RBTree),# 二叉树,数据结构

2. 红黑树的性质

  1. 每个结点不是红色就是黑色 ;
  2. 根节点是黑色的 ;
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 ;
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 ;
  5. 每个叶子结点都是黑色的( 此处的叶子结点指的是空结点(NIL节点) )。

这些约束确保了红黑树的关键特性从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。 因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

3. 红黑树节点的定义

// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;   // 节点的左孩子
	RBTreeNode<K, V>* _right;  // 节点的右孩子
	RBTreeNode<K, V>* _parent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
	pair<K, V> _kv;            // 节点的值
	Colour _col;               // 节点的颜色

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)  //默认为红节点
	{}
};

插入红色节点树的性质可能不会改变,而插入黑色节点每次都会违反性质4,所以 将节点设置为红色在插入时对红黑树造成的影响是小的,而黑色的影响是最大的

4. 红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索的树规则插入新节点;
  2. 检测新节点插入后,红黑树的性质是否造到破坏,如过性质被破坏,就要进行旋转或变色的操作,此时需要对红黑树分情况来讨论:
    以下为父节点在祖父的左边
    对于红黑树的插入来说,如果插入的父节点为黑,并且插入的节点默认为红节点,所以如果父节点为黑色时,插入是不需要进行调节的,如图1->2:

红黑树(RBTree),# 二叉树,数据结构
有2->3图可以看出cur、p两个红节点相连,所以需要调整,这种情况计为情况一:cur为红,p为红,g为黑,u存在且为红。(约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点)
unlce节点的作用: 在红黑树中,uncle节点是指当前节点的父节点的兄弟节点。它的作用是在插入新节点时,如果当前节点的父节点和uncle节点都是红色,那么需要将当前节点的父节点和uncle节点染成黑色,将当前节点的祖父节点染成红色,然后以祖父节点为当前节点进行下一轮操作。这样可以保证红黑树的性质4(每个红色节点必须有两个黑色的子节点)不被破坏。
对图3进行调整,需要保证红色节点不能相连,并且从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。如果只将cur改为黑色,那么违反规则4;将cur和uncle改为黑色,也会违反规则四,因为如果这棵树是一个子树,那么g结点的左右路径是会多一个黑色结点。那么如何调整?
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整(因为g的父节点可能为红色)。 这样保证了每条路径上的黑色结点的个数都相同。
对于变色来说,不论cur在parent的左边还是右边,变色后各个节点的位置都没有改变,所以不需要分类讨论。
代码如下:

while (parent && parent->_col == RED)
{
	Node* grandfather = parent->_parent;
	if (grandfather->_left == parent)
	{
		//情况1:uncle存在且为红
		Node* uncle = grandfather->_right;
		if (uncle && uncle->_col == RED)
		{
			grandfather->_col = RED;
			parent->_col = uncle->_col = BLACK;

			//继续向上调整
			cur = grandfather;
			parent = cur->_parent;
		}
	}
}

cur为parent的左边,如下图又该怎样调整?
红黑树(RBTree),# 二叉树,数据结构

说明: u的情况有两种
1.如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同;这时就需要旋转+变色处理。
2.如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。(如上图下半部分所示)
计上述情况为情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
对于上述两种情况我们发现怎么变色都是不行的,所以可以旋转+变色处理。旋转的详解如本篇博客(AVL树)
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红

cur为parent的右边,如下图又该怎样调整?
红黑树(RBTree),# 二叉树,数据结构
上述情况记为情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转则转换成了情况2。
如下图进行旋转+变色:
红黑树(RBTree),# 二叉树,数据结构

代码如下:

// 情况2和情况三3:u不存在/u存在且为黑,旋转+变色
   //     g
   //   p   u
   // c 
//cur为父亲节点的左边
if (cur == parent->_left)
{
	RotateR(grandfather);
	parent->_col = BLACK;
	grandfather->_col = RED;
}
//cur为父亲节点的右边,在右边,需要双旋(如AVL树中的旋转)
else
{
	//     g
	//   p   u
	//    c
	RotateL(parent);
	RotateR(grandfather);
	cur->_col = BLACK;
	grandfather->_col = RED;
}

对于父节点在祖父节点的右边,这种情况和上述大致相同,就不在多画了,完整代码如下。
RBTree.h文件中的代码:

#pragma once

enum Colour
{
	RED,
	BLACK,
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;   // 节点的左孩子
	RBTreeNode<K, V>* _right;  // 节点的右孩子
	RBTreeNode<K, V>* _parent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
	pair<K, V> _kv;            // 节点的值
	Colour _col;               // 节点的颜色

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}
		//寻找插入的位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (cur->_kv > kv)
			{
				cur = cur->_left;
			}
			else if (cur->_kv < kv)
			{
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		//插入新节点
		cur = new Node(kv);
		if (parent->_kv > kv)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;
		//进行调整这棵红黑树
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			//父节点在祖父的左边
			if (grandfather->_left == parent)
			{
				//情况1:uncle存在且为红
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					grandfather->_col = RED;
					parent->_col = uncle->_col = BLACK;

					//继续向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else  //叔叔不存在或叔叔存在且为黑,旋转+变黑
				{
					//cur为父亲节点的左边
					if (cur == parent->_left)
					{
						//     g
						//   p   u
						// c 
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else  //cur为父亲节点的右边
					{
						//     g
						//   p   u
						//    c 
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			//父节点在祖父的右边
			else // (grandfather->_right == parent)
			{
				//    g
				//  u   p
				//        c
				Node* uncle = grandfather->_left;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//    g
					//  u   p
					//        c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//    g
						//  u   p
						//     c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		//确保每次调整完后根节点为黑色
		_root->_col = BLACK;
		return true;
	}
	//因为红黑树的结点是new出来的,所以需要释放,也就是需要写析构函数
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}
	int Height()
	{
		return _Height(_root);
	}
	void InOrder()
	{
		_InOrder(_root);
	}
private:
	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		Node* ppnode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (ppnode == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subR;
			}
			else
			{
				ppnode->_right = subR;
			}
			subR->_parent = ppnode;
		}
	}
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* ppnode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}
			subL->_parent = ppnode;
		}
	}
	void _Destroy(Node* root)
	{
		if (root == nullptr)
			return;
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete(root);
	}
private:
	Node* _root = nullptr;
};

测试代码:

#include <iostream>
using namespace std;

#include "RBTree.h"

void Test_RBTree1()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	RBTree<int, int> t1;
	for (auto e : a)
	{
		t1.Insert(make_pair(e, e));
	}

	t1.InOrder();
}
int main()
{
	Test_RBTree1();
    return 0;
}

运行结果如下:
红黑树(RBTree),# 二叉树,数据结构

5. 红黑树与AVL树的比较

1.红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log N),但它们的实现方式不同。
2.红黑树的查询性能略微逊色于AVL树,因为其比AVL树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的AVL树最多多一次比较
3.红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,红黑树在插入和删除上优于AVL树,AVL树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于AVL树为了维持平衡的开销要小得多。 所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。文章来源地址https://www.toymoban.com/news/detail-651613.html

到了这里,关于红黑树(RBTree)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构大全】你想要的都有,数组、链表、堆栈、二叉树、红黑树、B树、图......

    作者简介: 目录 1.概述 2.线性结构 3.时间复杂度 4.查找算法 5.树 6.图 博主之前写过一个完整的关于数据结构的系列文章,一共十三篇,内容包含,数组、链表、堆栈、队列、时间复杂度、顺序查找、二分查找、二叉树、二叉搜索树、平衡二叉树、红黑树、B树、B+树、大顶堆

    2024年02月10日
    浏览(46)
  • 数据结构与算法(三):树论(树形结构、二叉树、二叉搜索树、红黑树、Btree&B+Tree、赫夫曼树、堆树)

    树论(树形结构、二叉树、二叉搜索树、红黑树、Btree、B+Tree、赫夫曼树、堆树) 在树形结构里面重要的术语: 结点:树里面的元素。 父子关系:结点之间相连的边 子树:当结点大于1时,其余的结点分为的互不相交的集合称为子树 度:一个结点拥有的子树数量称为结点的

    2024年02月01日
    浏览(94)
  • 数据结构之进阶二叉树(二叉搜索树和AVL树、红黑树的实现)超详细解析,附实操图和搜索二叉树的实现过程图

    绪论​ “生命有如铁砧,愈被敲打,愈能发出火花。——伽利略”;本章主要是数据结构 二叉树的进阶知识,若之前没学过二叉树建议看看这篇文章一篇掌握二叉树,本章的知识从浅到深的 对搜索二叉树的使用进行了介绍和对其底层逻辑的实现进行了讲解 ,希望能对你有所

    2024年02月04日
    浏览(47)
  • 【高阶数据结构】红黑树 {概念及性质;红黑树的结构;红黑树的实现;红黑树插入操作详细解释;红黑树的验证}

    红黑树(Red Black Tree) 是一种自平衡二叉查找树,在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。 AVL树 VS 红黑树 红黑树是

    2024年02月09日
    浏览(49)
  • 搜索二叉树 | 红黑树 | 验证是否为红黑树 | 代码实现

    黑红树是一颗特殊的搜索二叉树,本文在前文的基础上,图解红黑树插入:前文 链接,完整对部分关键代码展示,完整的代码在gitee仓库中: 链接 文章中有错误的地方,欢迎大家指正!如果有帮助到你,也请多多点赞支持! 1.红黑树的概述 平衡二叉树要求左右子树的高度差

    2024年02月21日
    浏览(41)
  • 数据结构 | 红黑树

    节点的左边比节点的值小,右边比节点的值大。 节点要么是 红色 ,要么是 黑色 根节点 是黑色 叶子节点都是黑色的空节点 红黑树中红色节点的子节点都是黑色 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点 在添加或者删除节点的时候,如果不满足这些性质会

    2024年01月21日
    浏览(44)
  • [数据结构]-红黑树

    前言 作者 : 小蜗牛向前冲 名言: 我可以接受失败,但我不能接受放弃   如果觉的博主的文章还不错的话,还请 点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、红黑树的基本知识  1、红黑树的概念 2、性质  二、红黑树的模拟实

    2024年02月04日
    浏览(46)
  • 【数据结构】红黑树

    🐱作者:一只大喵咪1201 🐱专栏:《数据结构与算法》 🔥格言: 你只管努力,剩下的交给时间! 在学习AVL树的时候,我们知道,当修改AVL树的结构(插入,删除)时,会通过旋转来保证平衡因子不超过1,所以频繁的修改结构会导致效率低下,今天我们学习的红黑树就完美解

    2023年04月17日
    浏览(49)
  • 数据结构——红黑树

    目录 概念 性质 结点的定义  插入 调整 当p是g的左孩子时 当p为g的右孩子时 插入完整代码 红黑树的检测 红黑树完整代码(包括测试数据)   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RED或BLACK。 通过对任何一条从根到叶子的路径

    2023年04月09日
    浏览(46)
  • 【数据结构-树】红黑树

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月07日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包