【C++ RB树】

news2024/12/26 12:01:04

文章目录

    • 红黑树
      • 红黑树的概念
      • 红黑树的性质
      • 红黑树节点的定义
      • 红黑树的插入
      • 代码实现
      • 总结

红黑树

AVL树是一颗绝对平衡的二叉搜索树,要求每个节点的左右高度差的绝对值不超过1,这样保证查询时的高效时间复杂度O( l o g 2 N ) log_2 N) log2N),但是要维护其绝对平衡,旋转的次数比较多。因此,如果一颗树的结构经常修改,那么AVL树就不太合适,所以就有了红黑树。

红黑树的概念

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

红黑树的性质

  1. 每个节点不是红色就是黑色
  2. 根节点是黑色的
  3. 不存在连续的红色节点
  4. 任意一条从根到叶子的路径上的黑色节点的数量相同
    根据上面的性质,红黑树就可以确保没有一条路径会比其他路径长出两倍,因为每条路径上的黑色节点的数量相同,所以理论上最短边一定都是黑色节点,最长边一定是一黑一红的不断重复的路径。

红黑树节点的定义

	enum Color
	{
		RED,
		BLACK
	};
	template<class K, class V>
	struct RBTreeNode
	{
		RBTreeNode<K, V>* _left;
		RBTreeNode<K, V>* _right;
		RBTreeNode<K, V>* _parent;
		Color _col;
		pair<K, V> _kv;
		RBTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_col(RED)
			,_kv(kv)
		{}
	};

插入新节点的颜色一定是红色,因为如果新节点的颜色是黑色,那么每条路径上的黑色节点的数量就不相同了,处理起来就比较麻烦,所以宁愿出现连续的红色节点,也不能让某一条路径上多出一个黑色节点。

红黑树的插入

1.根据二叉搜索树的规则插入新节点

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}
	Node* curr = _root;
	Node* parent = nullptr;
	while (curr)
	{
		if (curr->_kv.first < kv.first)
		{
			parent = curr;
			curr = curr->_right;
		}
		else if (curr->_kv.first > kv.first)
		{
			parent = curr;
			curr = curr->_left;
		}
		else
		{
			return false;
		}
	}
	curr = new Node(kv);
	if (parent->_kv.first < kv.first)
		parent->_right = curr;
	else
		parent->_left = curr;
	curr->_parent = parent;
........

2.测新节点插入后,红黑树的性质是否造到破坏

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}
	Node* curr = _root;
	Node* parent = nullptr;
	while (curr)
	{
		if (curr->_kv.first < kv.first)
		{
			parent = curr;
			curr = curr->_right;
		}
		else if (curr->_kv.first > kv.first)
		{
			parent = curr;
			curr = curr->_left;
		}
		else
		{
			return false;
		}
	}
	curr = new Node(kv);
	if (parent->_kv.first < kv.first)
		parent->_right = curr;
	else
		parent->_left = curr;
	curr->_parent = parent;

	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				curr = grandfather;
				parent = curr->_parent;
			}
			else
			{
				if (curr == parent->_left)
				{
					//      g
					//   p     u
					//c
					RotatoR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//      g
					//   p     u
					//    c
					RotatoL(parent);
					RotatoR(grandfather);
					curr->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		else
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				curr = grandfather;
				parent = curr->_parent;
			}
			else
			{
				if (curr == parent->_right)
				{
					//      g   
					//   u     p
					//           c
					RotatoL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//      g   
					//   u     p
					//        c
					RotatoR(parent);
					RotatoL(grandfather);
					curr->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		
	}
	_root->_col = BLACK;
	return true;
}
void RotatoL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	parent->_right = subRL;
	if (subRL)
		subRL->_parent = parent;
	subR->_left = parent;
	Node* ppnode = parent->_parent;
	parent->_parent = subR;
	if (parent == _root)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left == parent)
			ppnode->_left = subR;
		else
			ppnode->_right = subR;
		subR->_parent = ppnode;
	}
}
void RotatoR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	parent->_left = subLR;
	if (subLR)
		subLR->_parent = parent;
	subL->_right = parent;
	Node* ppnode = parent->_parent;
	parent->_parent = subL;
	if (parent == _root)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left == parent)
			ppnode->_left = subL;
		else
			ppnode->_right = subL;
		subL->_parent = ppnode;
	}
}

代码实现

#pragma once
#include <utility>

namespace lw
{
	enum Color
	{
		RED,
		BLACK
	};
	template<class K, class V>
	struct RBTreeNode
	{
		RBTreeNode<K, V>* _left;
		RBTreeNode<K, V>* _right;
		RBTreeNode<K, V>* _parent;
		Color _col;
		pair<K, V> _kv;
		RBTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_col(RED)
			,_kv(kv)
		{}
	};
	template<class K, class V>
	class RBTree
	{
		typedef RBTreeNode<K, V> Node;
	public:
		bool Insert(const pair<K, V>& kv)
		{
			if (_root == nullptr)
			{
				_root = new Node(kv);
				_root->_col = BLACK;
				return true;
			}
			Node* curr = _root;
			Node* parent = nullptr;
			while (curr)
			{
				if (curr->_kv.first < kv.first)
				{
					parent = curr;
					curr = curr->_right;
				}
				else if (curr->_kv.first > kv.first)
				{
					parent = curr;
					curr = curr->_left;
				}
				else
				{
					return false;
				}
			}
			curr = new Node(kv);
			if (parent->_kv.first < kv.first)
				parent->_right = curr;
			else
				parent->_left = curr;
			curr->_parent = parent;

			while (parent && parent->_col == RED)
			{
				Node* grandfather = parent->_parent;
				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						curr = grandfather;
						parent = curr->_parent;
					}
					else
					{
						if (curr == parent->_left)
						{
							//      g
							//   p     u
							//c
							RotatoR(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						else
						{
							//      g
							//   p     u
							//    c
							RotatoL(parent);
							RotatoR(grandfather);
							curr->_col = BLACK;
							grandfather->_col = RED;
						}
						break;
					}
				}
				else
				{
					Node* uncle = grandfather->_left;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						curr = grandfather;
						parent = curr->_parent;
					}
					else
					{
						if (curr == parent->_right)
						{
							//      g   
							//   u     p
							//           c
							RotatoL(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						else
						{
							//      g   
							//   u     p
							//        c
							RotatoR(parent);
							RotatoL(grandfather);
							curr->_col = BLACK;
							grandfather->_col = RED;
						}
						break;
					}
				}
				
			}
			_root->_col = BLACK;
			return true;
		}
		void RotatoL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			parent->_right = subRL;
			if (subRL)
				subRL->_parent = parent;
			subR->_left = parent;
			Node* ppnode = parent->_parent;
			parent->_parent = subR;
			if (parent == _root)
			{
				_root = subR;
				subR->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
					ppnode->_left = subR;
				else
					ppnode->_right = subR;
				subR->_parent = ppnode;
			}
		}
		void RotatoR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			parent->_left = subLR;
			if (subLR)
				subLR->_parent = parent;
			subL->_right = parent;
			Node* ppnode = parent->_parent;
			parent->_parent = subL;
			if (parent == _root)
			{
				_root = subL;
				subL->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
					ppnode->_left = subL;
				else
					ppnode->_right = subL;
				subL->_parent = ppnode;
			}
		}
		void InOrder()
		{
			_InOrder(_root);
		}
		bool IsBalance()
		{
			if (_root && _root->_col == RED)
				return false;
			Node* left = _root;
			int count = 0;
			while (left)
			{
				if (left->_col == BLACK)
					count++;
				left = left->_left;
			}
			return check(_root, 0, count);
		}
	private:
		bool check(Node* root, int count, int refBlackNumber)
		{
			if (root == nullptr)
			{
				if (count == refBlackNumber)
					return true;
				else
					return false;
			}
			if (root->_col == RED && root->_parent->_col == RED)
				return false;
			if (root->_col == BLACK)
				count++;
			return check(root->_left, count, refBlackNumber)
				&& check(root->_right, count, refBlackNumber);
		}
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_kv.first << " : " << root->_kv.second << endl;
			_InOrder(root->_right);
		}
		Node* _root = nullptr;
	};
}

总结

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

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

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

相关文章

钉钉小程序 - - - - - 如何通过一个链接打开小程序内的指定页面

方式1 钉钉小程序 scheme dingtalk://dingtalkclient/action/open_mini_app?miniAppId123&pagepages%2Findex%2Findex%3Fx%3D%25E4%25B8%25AD%25E6%2596%2587 方式2 https://applink.dingtalk.com/action/open_mini_app?type2&miniAppIdminiAppId&corpIdcorpId&…

MySQL行锁核心知识介绍

MySQL的行锁是数据库中用于控制并发访问的一种机制。它允许在数据库的行级别上实现锁定&#xff0c;从而允许多个事务同时修改不同行的数据&#xff0c;而不会相互干扰。这种锁机制可以提高数据库的并发性能&#xff0c;减少锁争用&#xff0c;提高事务的吞吐量。在本教程中&am…

一周学会Django5 Python Web开发-Jinja3模版引擎-安装与配置

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计35条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

.Net Core 中间件验签

文章目录 为什么是用中间件而不是筛选器&#xff1f;代码实现技术要点context.Request.EnableBuffering()指针问题 小结 为什么是用中间件而不是筛选器&#xff1f; 为什么要用中间件验签&#xff0c;而不是筛选器去验签? 1、根据上图我们可以看到&#xff0c;中间件在筛选器之…

Selenium控制已运行的Edge和Chrome浏览器——在线控制 | 人机交互(详细启动步骤和bug记录)

文章目录 前期准备1. 浏览器开启远程控制指令&#xff08;1&#xff09;Edge&#xff08;2&#xff09;Chrome 2. 执行python代码&#xff08;1&#xff09;先启动浏览器后执行代码&#xff08;2&#xff09;通过代码启动浏览器&#xff08;3&#xff09;Bug问题记录1&#xff…

前端和后端权限控制【笔记】

前端权限设置【笔记】 前言版权推荐前端权限设置需求效果实现资源 后端权限控制1.给所有前端请求都携带token2.添加拦截器3.配置到WebMvcConfiguration4.更多的权限验证 最后 前言 2024-3-15 18:27:26 以下内容源自《【笔记】》 仅供学习交流使用 版权 禁止其他平台发布时删…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:FormLink)

提供静态卡片交互组件&#xff0c;用于静态卡片内部和提供方应用间的交互&#xff0c;当前支持router、message和call三种类型的事件。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 该组件仅可…

Gitlab CI/CD 自动化打包部署前端(vue)项目

一、虚拟机安装 1.vmware下载 2.镜像下载 3.Ubuntu 4.新建虚拟机 一直点下一步&#xff0c;直到点击完成。 5.分配镜像 二、Gitlab CI/CD 自动化部署项目 1.配置GitLab CI/CD&#xff1a; A.在你的Vue.js项目中&#xff0c;创建一个名为.gitlab-ci.yml的文件&#xff0…

Javaweb--CSS

一&#xff1a;概述 CSS &#xff08;Cascading Style Sheet&#xff08;层叠样式表&#xff09;&#xff09;是一门语言&#xff0c;用于控制网页表现。 W3C标准规定了网页是由以下组成&#xff1a; 结构&#xff1a;HTML 表现&#xff1a;CSS 行为&#xff1a;JavaScrip…

【计算机网络】UDP/TCP 协议

TCP 协议 一、传输层1. 再谈端口号2. 端口号范围划分3. 进程和端口号4. netstat5. pidof 二、UDP 协议1. UDP 协议端格式(报文)2. UDP 的特点3. 面向数据报4. UDP 的缓冲区 三、TCP 协议1. 认识 TCP2. TCP 协议段格式&#xff08;1&#xff09;4 位首部长度&#xff08;2&#…

(三)丶RabbitMQ的四种类型交换机

前言&#xff1a;四大交换机工作原理及实战应用 1.交换机的概念 交换机可以理解成具有路由表的路由程序&#xff0c;仅此而已。每个消息都有一个称为路由键&#xff08;routing key&#xff09;的属性&#xff0c;就是一个简单的字符串。最新版本的RabbitMQ有四种交换机类型&a…

用真值表、逻辑表达式和卡诺图来表示数字电路中的逻辑关系

真值表&#xff08;Truth Table&#xff09; 真值表是一种直观的方式&#xff0c;通过列出所有可能的输入值及其对应的输出值来表示逻辑关系。以下是使用真值表表示逻辑关系的步骤&#xff1a; 1. 确定输入变量&#xff1a;列出数字电路中所有的输入变量。 2. 定…

CART决策树暴力生成风控规则(Python代码)

上一篇我们介绍了决策树节点信息更新的方法风控规则的决策树可视化&#xff08;升级版&#xff09;&#xff0c;以辅助我们制定风控规则&#xff0c;可视化的方法比较直观&#xff0c;适合做报告展示&#xff0c;但分析的时候效果没那么高。 本篇我们介绍一种通过决策树自动挖…

基于openresty构建运维工具链实践

本文字数&#xff1a;4591字 预计阅读时间&#xff1a;25 01 导读 如今OpenResty已广泛被各个互联网公司在实际生产环境中应用&#xff0c;在保留Nginx高并发、高稳定等特性基础上&#xff0c;通过嵌入Lua来提升在负载均衡层的开发效率并保证其高性能。本文主要介绍接口鉴权、流…

【Java】容器|Set、List、Map及常用API

目录 一、概述 二、List 1、List的常用API 2、ArrayList 3、List遍历 三、Set 1、Set的常用方法: 2、HashSet 3、遍历集合&#xff1a; 四、Map 1、Map常用API 2、HashMap 3、遍历Map 五、迭代器 一、概述 在Java中所有的容器都属于Collection接口下的内容 1…

D-泛醇(右泛醇)应用领域广泛 我国市场参与者众多

D-泛醇&#xff08;右泛醇&#xff09;应用领域广泛 我国市场参与者众多 D-泛醇又称右泛醇、原维生素B5&#xff0c;化学式为C9H19NO4&#xff0c;为泛醇的右旋异构体。D-泛醇外观呈无色粘稠或透明液体&#xff0c;微含臭味&#xff0c;可溶于甲醇、乙醇、水和丙二醇。D-泛醇综…

react native 实现自定义底部导航与路由文件配置

首先先把需要的一些库引入 yarn install react-navigation/native yarn install react-native-screens react-native-safe-area-context yarn install react-navigation/native-stack yarn add react-navigation/bottom-tabs 创建路由文件及四个底部导航页面 router文件下的bot…

MATLAB:一些杂例

a 2; b 5; x 0:pi/40:pi/2; %增量为pi/40 y b*exp(-a*x).*sin(b*x).*(0.012*x.^4-0.15*x.^30.075*x.^22.5*x); %点乘的意义 z y.^2; %点乘的意义 w(:,1) x; %组成w&#xff0c;第一列为x w(:,2) y; %组成w&#xff0c;第二列为y w(:,3) z; %组成w&#xff0c;第三列为z…

大规模C++程序设计 -- 基本规则

文章目录 基本规则概述成员数据访问全局命名空间全局数据自由函数枚举类型、typedef和常量数据预处理宏头文件中的名称 包含卫哨包含冗余卫哨文档标识符命名规则 基本规则 概述 任何精美的艺术不仅来源于创造&#xff0c;而且来自于规范。编程也是如此。C是易总大型语言&…

【New Release】PostgreSQL小版本(16.2, 15.6, 14.11, 13.14,12.18) 发布了

前言 PostgreSQL遵循小版本的发布规律&#xff0c;这一个季度的小版本又发布了。可以算作是2024年第一个季度的版本发布。如果总结其规律&#xff1a;大概就是2月、5月、8月、11月的样子。通常因为11月配合大版本的发布&#xff0c;它是起点&#xff0c;也有可能就是终点。起点…