红黑树的模拟实现

news2025/4/22 21:17:07

一、介绍

1. 概念

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

2. 性质

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

3. 结点定义

enum Colour//两种颜色
{
	RED,
	BLACK
};

//定义红黑树的结点
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	pair<K, V> _kv;
	Colour _col;

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

二、插入的3种情况

(一)情况1

  • 因为cur为当前插入新结点(红色),而不能有连在一起的红色节点,所以parent结点需要变成黑色
  • 控制每条路径上黑节点的数量相同,那么就要把uncle变黑
  • grandparent如果不是根节点,需要继续向上调整,所以grandparent需要变成红色

//情况1:uncle存在且为红色
if (uncle != nullptr && uncle->_col == RED)
{
	//调整颜色
	parent->_col = uncle->_col = BLACK;
	grandfather->_col = RED;

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

 

(二)情况2

//情况2
if (cur == parent->_left)
{
	//             grandfather
	//        parent
	//    cur
	RotateR(grandfather);//右旋转
	//调整颜色
	parent->_col = BLACK;
	grandfather->_col = RED;

}

 

(三)情况3

 

 

else//cur在parent的右边
{
	//             grandfather
	//        parent
	//              cur
	RotateL(parent);//先左旋转
	RotateR(grandfather);//再右旋转
	//调整颜色
	cur->_col = BLACK;
	grandfather->_col = RED;
}

(四)插入代码

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//已经存在一样的值,直接返回false
		{
			return false;
		}
	}

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

	//调整
	while (parent && parent->_col == RED)//如果父亲结点是黑色直接结束
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			//         grandfather
			//      parent     uncle
			//   cur
			//
			Node* uncle = grandfather->_right;
			//情况1:uncle存在且为红色
			if (uncle != nullptr && uncle->_col == RED)
			{
				//调整颜色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续往上调整
				cur = grandfather;
				parent = cur->_parent;
			}
			else//uncle不存在或者uncle为黑色
			{   //情况2
				if (cur == parent->_left)
				{
					//             grandfather
					//        parent
					//    cur
					RotateR(grandfather);//右旋转
					//调整颜色
					parent->_col = BLACK;
					grandfather->_col = RED;

				}
				else//cur在parent的右边
				{
					//             grandfather
					//        parent
					//              cur
					RotateL(parent);//先左旋转
					RotateR(grandfather);//再右旋转
					//调整颜色
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		else//parent == grandfather->_right
		{
			Node* uncle = grandfather->_left;

			//         g
			//      u    p
			//             c
			//
			//情况1:uncle存在且为红色
			if (uncle != nullptr && uncle->_col == RED)
			{
				//调整颜色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续往上调整
				cur = grandfather;
				parent = cur->_parent;
			}
			else//uncle不存在或者uncle为黑色
			{
				if (cur == parent->_right)
				{
					//           g
					//              p
					//                 c
					RotateL(grandfather);
					//调整颜色
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//         g
					//             p
					//           c
					RotateR(parent);//先右旋
					RotateL(grandfather);//再左旋

					//调整颜色
					cur->_col = BLACK;
					grandfather->_col = RED;

				}
				break;
			}
		}
	}
	_root->_col = BLACK;
	return true;
}

三、判断是否近似平衡

//	// 根节点->当前节点这条路径的黑色节点的数量
bool Check(Node* root, int blacknum, const int refVal)
{
	if (root == nullptr)//走到了一条路径的尽头
	{
		if (blacknum != refVal)
		{
			cout << "存在黑色节点数量不相等的路径" << endl;
			return false;
		}

		return true;
	}

	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << "有连续的红色节点" << endl;
		return false;
	}

	if (root->_col == BLACK)
	{
		++blacknum;
	}

	return Check(root->_left, blacknum, refVal)
		&& Check(root->_right, blacknum, refVal);
}

bool IsBalance()//判断是否平衡
{
	if (_root == nullptr)
		return true;

	if (_root->_col == RED)//根结点如果是红色
		return false;

	int refVal = 0;
	Node* cur = _root;
	while (cur)//计算其中一条路径上的黑色节点数量作为参考值
	{
		if (cur->_col == BLACK)
		{
			++refVal;
		}

		cur = cur->_left;
	}

	int blacknum = 0;
	return Check(_root, blacknum, refVal);
}

四、完整代码

#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;
enum Colour//两种颜色
{
	RED,
	BLACK
};

//定义红黑树的结点
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _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:
	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//已经存在一样的值,直接返回false
			{
				return false;
			}
		}

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

		//调整
		while (parent && parent->_col == RED)//如果父亲结点是黑色直接结束
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				//         grandfather
				//      parent     uncle
				//   cur
				//
				Node* uncle = grandfather->_right;
				//情况1:uncle存在且为红色
				if (uncle != nullptr && uncle->_col == RED)
				{
					//调整颜色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					
					//继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else//uncle不存在或者uncle为黑色
				{   //情况2
					if (cur == parent->_left)
					{
						//             grandfather
						//        parent
						//    cur
						RotateR(grandfather);//右旋转
						//调整颜色
						parent->_col = BLACK;
						grandfather->_col = RED;

					}
					else//cur在parent的右边
					{
						//             grandfather
						//        parent
						//              cur
						RotateL(parent);//先左旋转
						RotateR(grandfather);//再右旋转
						//调整颜色
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else//parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				
				//         g
				//      u    p
				//             c
				//
				//情况1:uncle存在且为红色
				if (uncle != nullptr && uncle->_col == RED)
				{
					//调整颜色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else//uncle不存在或者uncle为黑色
				{
					if (cur == parent->_right)
					{
						//           g
						//              p
						//                 c
						RotateL(grandfather);
						//调整颜色
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//         g
						//             p
						//           c
						RotateR(parent);//先右旋
						RotateL(grandfather);//再左旋

						//调整颜色
						cur->_col = BLACK;
						grandfather->_col = RED;

					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}

	// 根节点->当前节点这条路径的黑色节点的数量
	bool Check(Node* root, int blacknum, const int refVal)
	{
		if (root == nullptr)//走到了一条路径的尽头
		{
			if (blacknum != refVal)
			{
				cout << "存在黑色节点数量不相等的路径" << endl;
				return false;
			}

			return true;
		}
		
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "有连续的红色节点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			++blacknum;
		}

		return Check(root->_left, blacknum, refVal)
			&& Check(root->_right, blacknum, refVal);
	}

	bool IsBalance()//判断是否平衡
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)//根结点如果是红色
			return false;

		int refVal = 0;
		Node* cur = _root;
		while (cur)//计算其中一条路径上的黑色节点数量作为参考值
		{
			if (cur->_col == BLACK)
			{
				++refVal;
			}

			cur = cur->_left;
		}

		int blacknum = 0;
		return Check(_root, blacknum, refVal);
	}
	void RotateL(Node* parent)//左单旋
	{
		Node* parentParent = parent->_parent;
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		subR->_left = parent;

		//更新调整结点的父指针指向
		parent->_parent = subR;
		//subRL->_parent = parent;错误,没有判断subRL是不是为空
		if (subRL != nullptr)
		{
			subRL->_parent = parent;
		}

		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		//更新平衡因子
		//parent->_bf = subR->_bf = 0;
	}

	void RotateR(Node* parent)//右单旋
	{
		Node* parentParent = parent->_parent;
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		//更新调整结点的父指针指向
		if (subLR != nullptr)
		{
			subLR->_parent = parent;
		}

		subL->_right = parent;
		//更新调整结点的父指针指向
		parent->_parent = subL;


		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			//需要先判断subR应该链接在parentParent的哪一侧
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
		//更新平衡因子
		//parent->_bf = subL->_bf = 0;
	}
	void InOrder()//中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)//中序遍历
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

	int Height()
	{
		return _Height(_root);
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	size_t Size()
	{
		return _Size(_root);
	}

	size_t _Size(Node* root)
	{
		if (root == NULL)
			return 0;

		return _Size(root->_left)
			+ _Size(root->_right) + 1;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return NULL;
	}
private:
	Node* _root = nullptr;
};

1. 测试用例1

#include"RBTree.h"
int main()
{
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	int a[] = { 790,760,969,270,31,424,377,24,702 };
	RBTree<int, int> t;
	for (auto e : a)
	{
		if (e == 702)
		{
			int i = 0;
		}
	
		cout << "Insert:" << e << "->";
		t.Insert(make_pair(e, e));
		cout << t.IsBalance() << endl;
	}
	t.InOrder();
	
	cout << t.IsBalance() << endl;
	
	return 0;
}

2. 测试用例2

#include"RBTree.h"

int main()
{
	const int N = 100000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
		//cout << v.back() << endl;
	}

	size_t begin2 = clock();
	RBTree<int, int> t;
	for (auto e : v)
	{
		if (e == 29365)
		{
			int i = 0;
		}

		//cout << "Insert:" << e << "->";
		t.Insert(make_pair(e, e));
		//cout << t.IsBalance() << endl;
	}
	size_t end2 = clock();

	cout << "Insert:" << end2 - begin2 << endl;

	cout << t.IsBalance() << endl;
	cout << t.Height() << endl;
	cout << t.Size() << endl;

	size_t begin1 = clock();
	for (auto e : v)
	{
		t.Find(e);
	}

	for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}

	size_t end1 = clock();

	cout << "Find:" << end1 - begin1 << endl;

	return 0;
}

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

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

相关文章

超级有效的12个远程团队管理技巧

前言 随着远程办公的兴起&#xff0c;虚拟管理团队已成为新常态。尽管混合和远程工作正在成为新常态&#xff0c;但管理远程团队也面临着一系列挑战。本文我们将为您提供 12个技巧&#xff0c;帮助您成功管理远程团队并改善协作。此外&#xff0c;我们将讨论定期接触点的重要性…

前端通过导入editor.md库实现markdown功能

小王学习录 今日摘录前言jquery下载editor下载editor和jquery的导入初始化editor总结 今日摘录 满招损&#xff0c;谦受益 前言 要想通过editor.md实现markdown的功能&#xff0c;需要经过如下四步&#xff1a; 下载editor.md到本地将本地editor导入到前端代码中编写少量代…

【分布式】tensorflow 1 分布式代码实战与说明;单个节点上运行 2 个分布式worker工作线程

tensorflow.python.framework.errors_impl.UnknowError: Could not start gRPC server 1. tf分布式 一台电脑服务器server是一个节点&#xff0c;包含了多个GPU。首先分布式的方式就是让多台电脑上的gpu共同干活。 分布式工作分为两个部分&#xff0c;parameter server&#…

Practice01-Qt6.0设置文本颜色、格式等。

Qt6.0学习&#xff0c;在此做个记录&#xff0c;方便日后查找复习 本次项目用到的控件有&#xff1a;复选框&#xff0c;单选按钮。文本编辑框。 项目目录结构&#xff1a; 项目运行效果图&#xff1a; 实现的功能&#xff1a; 勾选Underline、Italic&#xff0c;Bold时&…

BUUCTF——刮开有奖

打开程序&#xff1a; 就一个这个玩意儿&#xff0c;没有输入框&#xff0c;没有啥的&#xff0c;打开IDA反编译一下&#xff1a; 直接找到WinMain&#xff0c;发现里面只有一个对话框API&#xff08;如果只有一个对话框&#xff0c;那真就没有输入框了&#xff09;&#xff0…

人工智能基础——Python:Matplotlib与绘图设计

人工智能的学习之路非常漫长&#xff0c;不少人因为学习路线不对或者学习内容不够专业而举步难行。不过别担心&#xff0c;我为大家整理了一份600多G的学习资源&#xff0c;基本上涵盖了人工智能学习的所有内容。点击下方链接,0元进群领取学习资源,让你的学习之路更加顺畅!记得…

Android---屏幕适配的处理技巧

在几年前&#xff0c;屏幕适配一直是困扰 Android 开发工程师的一大问题。但是随着近几年各种屏幕适配方案的诞生&#xff0c;以及谷歌各种适配控件的推出&#xff0c;屏幕适配也显得越来越容易。下面&#xff0c;我们就来总结一下关于屏幕适配的那些技巧。 ConstraintLayout …

【数据结构】二叉树经典例题---<你真的掌握二叉树了吗?>(第一弹)

一、已知一颗二叉树如下图&#xff0c;试求&#xff1a; (1)该二叉树前序、中序和后序遍历的结果。 (2)该二叉树是否为满二叉树&#xff1f;是否为完全二叉树&#xff1f; (3)将它转换成对应的树或森林。 (4)这颗二叉树的深度为多少? (5)试对该二叉树进行前序线索化。 (6)试对…

向量的点积和外积

参考&#xff1a;https://www.cnblogs.com/gxcdream/p/7597865.html 一、向量的内积&#xff08;点乘&#xff09; 定义&#xff1a; 两个向量a与b的内积为 ab |a||b|cos∠(a, b)&#xff0c;特别地&#xff0c;0a a0 0&#xff1b;若a&#xff0c;b是非零向量&#xff0c;…

Shopee收款账户怎么设置?shopee收款方式选哪种

Shopee作为一家领先的电子商务平台&#xff0c;为卖家提供了多种收款方式。无论是在线支付、虚拟账户余额还是线下支付&#xff0c;卖家可以根据自己的需求和交易情况来进行选择。然而&#xff0c;在选择收款方式时&#xff0c;安全性、便捷性和市场适应性是需要考虑虾皮Shopee…

【Git】Git的GUI图形化工具ssh协议IDEA集成Git

一、GIT的GUI图形化工具 1、介绍 Git自带的GUI工具&#xff0c;主界面中各个按钮的意思基本与界面文字一致&#xff0c;与git的命令差别不大。在了解自己所做的操作情况下&#xff0c;各个功能点开看下就知道是怎么操作的。即使不了解&#xff0c;只要不做push操作&#xff0c;…

【数据结构】顺序表 | 详细讲解

在计算机中主要有两种基本的存储结构用于存放线性表&#xff1a;顺序存储结构和链式存储结构。本篇文章介绍采用顺序存储的结构实现线性表的存储。 顺序存储定义 线性表的顺序存储结构&#xff0c;指的是一段地址连续的存储单元依次存储链性表的数据元素。 线性表的&#xf…

Activiti BPMN visualizer Using Of Idear

Launch 安装插件 创建文件 可视化创建按钮 设置条件,是在线上设置的

【C++破局】C++内存管理之new与deleted剖析

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;c内存管理部分知识点梳理 &#x1f449;【C-C入门系列专栏】&#xff1a;博客文章专栏传送门 &#x1f604;每日一言&#xff1a;花有重开日&#xff0c;人无再少年&#xff01; 目录 C/C的内存分配机…

Vue中的常用指令v-html / v-show / v-if / v-else / v-on / v-bind / v-for / v-model

前言 持续学习总结输出中&#xff0c;Vue中的常用指令v-html / v-show / v-if / v-else / v-on / v-bind / v-for / v-model 概念&#xff1a;指令&#xff08;Directives&#xff09;是Vue提供的带有 v- 前缀 的特殊标签属性。可以提高操作 DOM 的效率。 vue 中的指令按照不…

Java Web——HTTP协议

目录 1. HTTP协议概述 1.1. HTTP数据传输格式 1.2. HTTP协议特点 2. HTTP 1.0和HTTP 1.1 3. HTTP请求协议 3.1. GET方式请求协议 3.2. POST方式请求协议 3.3. GET请求和POST请求的区别 4. HTTP相应协议 4.1. 响应状态码 如果两个国家进行会晤需要遵守一定的礼节。所以…

WMS配送中心主要业务流程

业务流程图 入库 波次出库 按门店和门店所属送货路线确定出库波次 入库 出库 移库、封仓 门店欠货能要点 1. 日常补货&#xff1a;分拣仓位商品小于当前商品在该位置的补货下限的时候&#xff1b;生成对此进行补货任务&#xff1b;补货完成后确认任务&#xff0c;系统变更库存…

win10使用mingw安装OpenCV4.8

1. cmake安装 下载链接如下https://github.com/Kitware/CMake/releases/download/v3.27.7/cmake-3.27.7-windows-x86_64.zip 解压后放到指定目录后&#xff0c;添加bin目录到环境变量即可。 2. mingw安装 下载链接如下(下图的x86_64-posix-sjlj)&#xff1a; Download x86_…

DevChat:提升编程效率的AI编程助手

一、前言 1、当前开发的痛点&#x1f616; 在软件开发过程中&#xff0c;开发者经常需要编写复杂的代码&#xff0c;如数据结构、算法、网络通信等&#xff0c;这些都需要耗费大量的时间和精力。同时&#xff0c;不同的编程语言和框架也会给开发者带来许多不便&#xff0c;例如…

Hadoop入门——数据分析基本步骤

文章目录 1.概述2.分析步骤2.1第一步 明确分析目的和思路2.2第二步 数据收集2.3第三步 数据处理2.4第四步 数据分析2.5第五步 数据展现2.6第六步 报告撰写 3.总结 1.概述 2.分析步骤 2.1第一步 明确分析目的和思路 2.2第二步 数据收集 2.3第三步 数据处理 2.4第四步 数据分析 …