红黑树——原理刨析

news2024/12/28 23:09:12

        众所周知,红黑树是从AVLTree树中衍变而来的,所以在学红黑树之前还是要好好的理解一下AVLTree树的原理,为理解红黑树减轻理解负担,好了进入正题。

红黑树原理:

        由名可知,红黑树——肯定是与颜色有关的一个树,又因为是从AVLTree树中衍化过来的,所以也是搜索树(不是平衡二叉树,后面讲解定义时会详细解释),通过对不同情况的处理,去调整红黑树节点的颜色或者红黑树的高度去使其满足,红黑树的定义规则。

红黑树的定义:

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

如上图,就是红黑树。

红黑树的性质:

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

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

答:上述红黑树的性质第4条 说明每条路上面的黑色节点数量都是相等的,所以说该节点的左右子树可以有一棵子树全为黑色节点 另一个红黑节点交替(红节点数量与黑节点数量相等),这就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍。

红黑树中重要的函数讲解:

        和AVLTree一样,插入函数是难点,但是掌握AVLTree之后,这里的插入就不怎么难了,AVLTree中提到左旋,右旋,这里不做讲解,如有疑惑,参考上篇文章,有流程图

        在我看来红黑树与AVLTree不同点就是规则不同,红黑树是靠颜色去调整高度差,而AVLTree是通过平衡因子去调节的。

情况一:整棵树或者子树为上上图,就只能进行颜色更新 将uncle更新为黑色 父亲更新为黑色 祖父更新为红色 再继续向上以同样的方式更新 直到更新到根节点或者进行了一次旋转调整 (旋转调整会将树的高度改变并将颜色确定为最终的颜色)就不再向上更新

情况二:uncle存在且为黑或者不存在

1,先进行情况一的颜色更新,出现了旋转的情况,再进行旋转 最后进行旋转的颜色更新

               

        2,刚开始整棵树就为要旋转的情况或者为整棵树的子树,如上图

单选和双旋在AVLTree中已经讲解过了,这里最重要的就是如何进行颜色更新,而不是旋转。

        

红黑树完整代码:

#pragma once
#include<iostream>
using namespace std;

enum color
{
	BLACK,
	RED
};

template<class K,class V>
struct RBTreeNode
{

	RBTreeNode<K, V>* _parent;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	pair<K, V> _kv;
	color _col;

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

	}
};

template<class K,class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
private:
	Node* _root = nullptr;

public:
	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)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//存在就不插入
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED;
		if (parent->_kv.first < cur->_kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_parent;
			assert(grandfater);

			//祖父颜色不为黑 说明红黑树在插入之前就是不平衡的
			assert(grandfater->_col == BLACK);
			if (parent == grandfater->_left)
			{
				Node* uncle = grandfater->_right;
				if (uncle && uncle->_col == RED)
				{
					grandfater->_col = RED;
					parent->_col = uncle->_col = BLACK;

					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_left == cur)
					{
						//    g
						//  p   u
						//c
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						//    g
						//  p   u
						//    c
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandfater->_left;
				if (uncle && uncle->_col == RED)
				{
					grandfater->_col = RED;
					parent->_col = uncle->_col = BLACK;

					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_right == cur)
					{
						//    g
						//  u   p
						//        c
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						//    g
						//  u   p
						//     c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}
	void Inorder()
	{
		_Inorder(_root);
	}
	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return false;
		}
		if (_root->_col == RED)
		{
			cout << "根节点为红色,不是红黑树" << endl;
			return false;
		}
		int benchmark = 0;
		return PrevCheck(_root, 0, benchmark);
	}
	private:
		bool PrevCheck(Node* root, int blackNum, int& benchmark)
		{
			if (root == nullptr)
			{
				if (benchmark == 0)
				{
					blackNum = benchmark;
					return true;//第一次遍历到空 没有比较意义 将第一次的黑色节点作为参考去判断
				}
				if (blackNum != benchmark)
				{
					cout << "红黑树各路黑色节点数量不相同" << endl;
					return false;
				}
				else
				{
					return true;
				}
			}
			if (root->_col == BLACK)
			{
				blackNum++;
			}
			if (root->_col == RED && root->_parent->_col == RED)
			{
				cout << "出现连续红色节点,不是红黑树" << endl;
				return false;
			}

			return PrevCheck(root->_left,blackNum,benchmark) 
				&& PrevCheck(root->_right,blackNum,benchmark);
		}
		void _Inorder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Inorder(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_Inorder(root->_right);
		}
	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 (ppNode == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
				subL->_parent = ppNode;
			}
			else
			{
				ppNode->_right = subL;
				subL->_parent = ppNode;
			}
		}
	}
	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;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
	}
};
void TestRBTree1()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 0,5,30,25,20,4,13,30,28,27 };  // 测试双旋平衡因子调节
	//int a[] = { 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();
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

void TestRBTree2()
{
	size_t N = 1000;
	srand(time(0));
	RBTree<int, int> t1;
	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();
		cout << "Insert:" << x << ":" << i << endl;
		t1.insert(make_pair(x, i));
	}
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1176284.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通讯录详解(静态版,动态版,文件版)

&#x1f493;博客主页&#xff1a;江池俊的博客⏩收录专栏&#xff1a;C语言进阶之路&#x1f449;专栏推荐&#xff1a;✅C语言初阶之路 ✅数据结构探索✅C语言刷题专栏&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f389;欢迎大家点赞&#x1f44d;评论&#x…

第三章:boundary-value analysis

文章目录 Boundary-value Analysiscomputational faults 计算错误boundary shift 边界偏移boundary value analysis 的优势Path condition, domain, and domain boundary (路径条件、域和域边界)Open and closed boundaries (闭合边界 / 开放边界)on / off pointGuidelinestr…

浅析LiveMedia智能视频网关的AI识别技术及应用场景

一、行业背景 &#xff08;1&#xff09;AI技术在安防领域大量落地应用 随着近几年人工智能的快速发展&#xff0c;深度学习方法及性能日益提升&#xff0c;计算机视觉、图像处理、视频结构化和大数据分析等技术也不断完善&#xff0c;使得安防产品逐步走向智能化。在技术成熟…

redis五种数据类型

Redis支持五种数据类型&#xff1a;string&#xff08;字符串&#xff09;&#xff0c;hash&#xff08;哈希&#xff09;&#xff0c;list&#xff08;列表&#xff09;&#xff0c;set&#xff08;集合&#xff09;及zset(sorted set&#xff1a;有序集合)。 1.String&#…

Rust结构体和枚举类

文章目录 元组结构体结构体枚举类 Rust初步上手⚙所有权 元组结构体 元组结构体是最简单的结构体&#xff0c;可以粗暴地理解为是有名字的元组&#xff0c;二者的区别如下。 let tup: (i32, f64, u8) (500, 6.4, 1);struct Test(i32, f64, u8); let t Test(500,6.4,1)第一…

操作系统——文件在外存中的分配方式(王道视频p61 P62)

1.总体概述&#xff1a; 连续分配 —— 链接分配 —— 索引分配 &#xff08;1&#xff09;对于顺序分配&#xff0c;这种方式 基本不会使用了&#xff0c; 因为 它存在一个 核心的问题就是 没法更新&#xff1b;不过&#xff0c;还是要注意它的 “文件目录”——其中存放了…

Memcached构建缓存服务器

Memcache介绍 1、特点 内置存储方式----------为了提高性能&#xff0c;memcached中保存的数据都存储在memcache内置的内存存储空间中。由于数据仅存在于内存中&#xff0c;重启操作系统会导致全部数据消失 简单key/value存储--------------服务器不关心数据本身的意义及结构&…

NSSCTF web刷题记录4

文章目录 [NSSRound#4 SWPU]1zweb(revenge)[强网杯 2019]高明的黑客[BJDCTF 2020]Cookie is so subtle![MoeCTF 2021]fake game[第五空间 2021]PNG图片转换器[ASIS 2019]Unicorn shop[justCTF 2020]gofs[UUCTF 2022 新生赛]phonecode[b01lers 2020]Life On Mars[HZNUCTF 2023 f…

【qemu逃逸】GACTF2020-babyqemu

前言 虚拟机用户名&#xff1a;root 无密码 设备逆向 题目去掉的符号&#xff0c;经过逆向分析&#xff0c;实例结构体如下&#xff1a; 可以看到 arr_int_8 数组后面存在一个函数指针&#xff0c;不用想基本上就是劫持该函数指针了。 denc_mmio_read 函数 这里存在越界读…

.net core 到底行不行!超高稳定性和性能的客服系统:性能实测

业余时间用 .net core 写了一个升讯威在线客服系统。并在博客园写了一个系列的文章&#xff0c;介绍了这个开发过程。 我把这款业余时间写的小系统丢在网上&#xff0c;陆续有人找我要私有化版本&#xff0c;我都给了&#xff0c;毕竟软件业的初衷就是免费和分享&#xff0c;后…

最新知识付费变现小程序源码/独立后台知识付费小程序源码/修复登录接口

最新知识付费变现小程序源码&#xff0c;独立后台知识付费小程序源码&#xff0c;最新版修复登录接口。 主要功能 会员系统&#xff0c;用户登录/注册购买记录 收藏记录 基本设置 后台控制导航颜色 字体颜色 标题等设置 流量主广告开关小程序广告显示隐藏 广告主审核过审核…

VS2022创建win32汇编项目

文章目录 一、下载安装win32环境1.1、下载网址&#xff1a;https://masm32.com/1.2、解压缩安装1.3、安装路径1.4、安装masm32 SDK1.5、安装成功1.6、导入lib1.7、配置默认&#xff0c;可以根据自己需求修改1.8、启动界面二、vs2022 安装过程略过。。。2.1、创建项目2.2、填写项…

高通Android 8.1 扫码枪无法扫sn包含2或者全部是2的问题

背景&#xff1a;由于近期工厂生产&#xff0c;测试突然反馈扫码枪扫sn总是丢失2&#xff0c;比如 AXB2SHS822009997/LSXG 结果显示是 AXBSHS800997/LSX 于是我叫测试找了之前可以版本然后抓日志进行对比发现&#xff0c;确实只有2这个数字无法扫&#xff0c;如果把2这一位改成…

California Science Museum

文章目录 1. University of Southern California(USC)2. NASA航天飞机3. 返回舱4. Others彩蛋1: Paris, capital of France彩蛋2: Switzerland(瑞士)1. University of Southern California(USC) 2. NASA航天飞机

运动耳机品牌排行榜,推荐几款优秀的运动耳机

​说起耳机&#xff0c;相信大家都比较熟悉&#xff0c;特别是对于喜欢运动的爱好人士来说&#xff0c;那更是随身携带着。随着运动耳机的增长&#xff0c;大家都不知道该如何选择了。对于运动耳机除了需要佩戴稳固舒适之外&#xff0c;还有就是音质表现、防水性能、通话质量等…

外汇天眼实勘功能升级,带你沉浸式“云”穿交易商现场!

最近&#xff0c;外汇天眼新出了一个功能&#xff0c;这个功能可了不得了&#xff0c;不管你在国外还是在国内&#xff0c;它都能带你走进交易商现场。不过在介绍该功能之前&#xff0c;天眼君先问大家几个问题&#xff1a;在进行外汇交易前&#xff0c;你对自己的交易平台了解…

项目启动∣得益乳业引进企企通采购供应链管理+智采商城平台,切实提升供应链效率

近日&#xff0c;山东得益乳业股份有限公司&#xff08;以下简称“得益乳业”&#xff09;与企企通成功召开采购供应链管理智采商城双项目启动会。双方高层领导及项目团队关键成员&#xff0c;一同出席本次启动会。 本次合作以企企通数字化采购解决方案为基础&#xff0c;结合得…

基于 golang 从零到一实现时间轮算法 (三)

引言 本文参考小徐先生的相关博客整理&#xff0c;项目地址为&#xff1a; https://github.com/xiaoxuxiansheng/timewheel/blob/main/redis_time_wheel.go。主要是完善流程以及记录个人学习笔记。 分布式版实现 本章我们讨论一下&#xff0c;如何基于 redis 实现分布式版本的…

Java零基础手把手保姆级教程_类和对象(超详细)

文章目录 Java零基础手把手保姆级教程_类和对象&#xff08;超详细&#xff09;1. 类和对象1.1 类和对象的理解1.2 类的定义1.3 对象的使用1.4 学生对象-练习1.5测测你掌握了没&#xff1f; 2. 对象内存图2.1 单个对象内存图2.2 多个对象内存图2.3 多个对象指向相同内存图 3. 成…

从首届中国测绘地理信息大会,解读2023年度国产GIS创新关键词

创新是什么&#xff1f;这是各行各业持续思考的问题。 第一届中国测绘地理信息大会已进入倒计时&#xff01;这是中国测绘学会、中国地理信息产业协会和中国卫星导航定位协会共同主办的全国性高端盛会。据悉&#xff0c;本次大会将有1个主论坛、38场分论坛&#xff0c;近2万平…