红黑树-RBTree

目录

  • 1. 红黑树的概念
  • 2. 红黑树的性质
  • 3. 结点的定义
  • 4. 结点的插入
  • 5. 整体代码

1. 红黑树的概念

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

最短路径:全黑;最长路径:一黑一红交替。

由于avl树要求严格的平衡,因此相比于红黑树来说需要更频繁的旋转来保证平衡,所以整体而言效率是比红黑树要低一点。

在这里插入图片描述

2. 红黑树的性质

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

3. 结点的定义

//枚举颜色
enum Color {
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode {
	RBTreeNode(const pair<const K, V>& kv)
		:_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED)
	{ }
	pair<const K, V> _kv;
	//需要再增加一个结点颜色信息
	Color _col;
	RBTreeNode<const K, V>* _left;
	RBTreeNode<const K, V>* _right;
	//同样是需要父亲结点,后续调整旋转需要找到父亲
	RBTreeNode<const K, V>* _parent;
};

4. 结点的插入

往已经是满足红黑树性质的树中插入一个结点时,待插入结点的颜色一定要设置为红色,为什么?

若插入一个黑色结点,那么必然会违反性质4,若插入红色节点则不一定会违反性质3。

红黑树插入的关键在于要看叔叔结点的颜色,具体情况可分为两种:

  1. 叔叔存在且为红
    在这里插入图片描述
  2. 叔叔不存在或者叔叔存在且为黑
    在这里插入图片描述
    叔叔存在且为黑是第一种情况触发后才会出现:
    在这里插入图片描述

5. 整体代码

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

#pragma once
#include <iostream>

using namespace std;

enum Color {
	RED,
	BLACK
};

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

	}
	pair<const K, V> _kv;
	enum Color _col;
	RBTreeNode<const K, V>* _left;
	RBTreeNode<const K, V>* _right;
	RBTreeNode<const K, V>* _parent;
};

template<class K, class V>
class RBTree {
	typedef RBTreeNode<const K, V> Node;
public:
	bool insert(const pair<const K, V>& kv) {
		if (!_root) {
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}
		
		Node* cur = _root;
		Node* parent = nullptr;
		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);
		if (parent->_kv.first < kv.first) {
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		cur->_parent = parent;

		//parent为红需要一直往上调整
		while (parent && parent->_col == RED) {
			Node* grandpa = parent->_parent;
			if (grandpa->_left == parent) {
				Node* uncle = grandpa->_right;
				//叔叔存在且为红
				if (uncle && uncle->_col == RED) {
					//把父亲和叔叔染黑
					//爷爷染红继续往上调整
					parent->_col = uncle->_col = BLACK;
					grandpa->_col = RED;

					cur = grandpa;
					parent = cur->_parent;
				}
				//叔叔不存在或者存在且为黑
				else {
					//单纯的一边高(直线)单旋
					if (cur == parent->_left) {
						rotateRight(grandpa);
						parent->_col = BLACK;
						cur->_col = grandpa->_col = RED;
					}
					//折线情况需要双旋
					else {
						rotateLeft(parent);
						rotateRight(grandpa);
						cur->_col = BLACK;
						parent->_col = grandpa->_col = RED;
					}
					break;
				}
			}
			//同样的逻辑
			else {
				Node* uncle = grandpa->_left;
				if (uncle && uncle->_col == RED) {
					parent->_col = uncle->_col = BLACK;
					grandpa->_col = RED;
					
					cur = grandpa;
					parent = cur->_parent;
				}
				else {
					if (parent->_right == cur) {
						rotateLeft(grandpa);
						grandpa->_col = cur->_col = RED;
						parent->_col = BLACK;
					}
					else {
						rotateRight(parent);
						rotateLeft(grandpa);
						parent->_col = grandpa->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}		 
			}
		}
		_root->_col = BLACK;
		return true;
	}

	bool isBlance() {
		if (_root->_col != BLACK) {
			return false;
		}
		int cnt = 0;
		Node* cur = _root;
		while (cur) {
			cnt += cur->_col == BLACK;
			cur = cur->_left;
		}
		return _isBlance(_root, 0, cnt);
	}
	
	int getHeight() {
		return getHeight(_root);
	}
	
private:
	int getHeight(Node* root) {
		if (!root) {
			return 0;
		}
		int leftH = getHeight(root->_left);
		int rightH = getHeight(root->_right);
		return max(leftH, rightH) + 1;
	}
	bool _isBlance(Node* root, int blackcnt, int t) {
		if (!root) {
			cout << blackcnt << ' ' << t << endl;
			return t == blackcnt;
		}
		if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
			return false;
		}
		return _isBlance(root->_left, blackcnt + (root->_col == BLACK), t) && _isBlance(root->_right, blackcnt + (root->_col == BLACK), t);
	}

	void rotateLeft(Node* parent) {
		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		if (curleft) {
			curleft->_parent = parent;
		}
		parent->_right = curleft;
		cur->_left = parent;

		Node* oldparent = parent->_parent;
		parent->_parent = cur;
		cur->_parent = oldparent;

		if (!oldparent) {
			_root = cur;
		}
		else {
			if (oldparent->_left == parent) {
				oldparent->_left = cur;
			}
			else {
				oldparent->_right = cur;
			}
		}
	}

	void rotateRight(Node* parent) {
		Node* cur = parent->_left;
		Node* curright = cur->_right;

		if (curright) {
			curright->_parent = parent;
		}
		parent->_left = curright;
		cur->_right = parent;

		Node* oldparent = parent->_parent;
		parent->_parent = cur;
		cur->_parent = oldparent;
		if (!oldparent) {
			_root = cur;
		}
		else {
			if (oldparent->_left == parent) {
				oldparent->_left = cur;
			}
			else {
				oldparent->_right = cur;
			}
		}
	}

	
private:
	Node* _root = nullptr;
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Echarts柱状体实现滚动条动态滚动

当我们柱状图中X轴数据太多的时候&#xff0c;会自动把柱形的宽度挤的很细&#xff0c;带来的交互非常不好&#xff0c;因此就有一个属性来解决&#xff1a;dataZoom 第一种简易的版本&#xff0c;横向滚动。 dataZoom: {show: true, // 为true 滚动条出现realtime: true, // 实…

第七章 块为结构建模 P4|系统建模语言SysML实用指南学习

仅供个人学习记录 这部分感觉很模糊&#xff0c;理解的不好&#xff0c;后面的图也没画了&#xff0c;用到的时候再来翻书 应用端口实现接口建模 端口port表示了块边界上的一个访问点&#xff0c;也可以是由该块分类的任何组成或引用边界上的可访问点。一个块可以有多个端口规…

Java学习 10.Java-数组习题

一、创建一个 int 类型的数组, 元素个数为 100, 并把每个元素依次设置为 1 - 100 代码实现 public static void main(String[] args) {int[] arrnew int[100];for (int i 0; i < arr.length; i) {arr[i]i1;}System.out.println(Arrays.toString(arr));} 运行结果 二、改变…

指针传 1

1. 内存 在计算机中内存划分为⼀个个的内存单元&#xff0c;每个内存单元的⼤⼩取1个字节。每个内存单元放了八个bite位&#xff0c;就像我们在高中时住的八人间&#xff0c;那么每个人就代表了一个bite位。 每个内存单元也都有⼀个编号&#xff08;这个编号就相当 于我们所住…

2000-2022年上市公司数字化转型同群效应数据

2000-2022年上市公司数字化转型同群效应数据 1、时间&#xff1a;2000-2022年 2、指标&#xff1a;股票代码、年份、行业代码、行政区划代码、数字化转型程度-A、数字化转型程度-B、同行业同群-数字化转型程度-A_均值、同行业同群-数字化转型程度-A_中位数、同省份同群-数字化…

odoo16 库存初始化 excel导入问题

最近在为一家公司实施odoo时&#xff0c;发现库存模块实施过程中按用户实际&#xff0c;产品初始化就是个问题。下面一一记录下 一个新公司&#xff0c;产品都有上百种&#xff0c;甚致几千种&#xff0c;如何把现有产品数据录入系统就是个不小的活。odoo16是有导入导出功能不…

1994-2021年分行业二氧化碳排放量数据

1994-2021年分行业二氧化碳排放量数据 1、时间&#xff1a;1994-2021年 2、来源&#xff1a;原始数据整理自能源年鉴 3、指标&#xff1a;统计年度、行业代码、行业名称、煤炭二氧化碳排放量、焦炭二氧化碳排放量、原油二氧化碳排放量、汽油二氧化碳排放量、煤油二氧化碳排放…

管理能力测评,如何提升管理能力?

管理能力是综合能力的体现&#xff0c;通常也解读为组织管理能力&#xff0c;如果要再细分的话&#xff0c;可能还包括有沟通能力&#xff0c;协调能力&#xff0c;组织能力&#xff0c;执行力和专业能力等等。不过没有办法说的太细节&#xff0c;因为每个部分铺开了都是一个独…

基于51单片机RFID射频门禁刷卡系统设计

**单片机设计介绍&#xff0c; 基于51单片机RFID射频门禁刷卡系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序程序 六、 文章目录 一 概要 基于51单片机RFID射频门禁刷卡系统&#xff0c;是一种将单片机技术和射频标识技术应用于门禁控制系统的…

自主开发刷题应用网站H5源码(无需后端无需数据库)

该应用使用JSON作为题库的存储方式&#xff0c;层次清晰、结构简单易懂。 配套的word模板和模板到JSON转换工具可供使用&#xff0c;方便将题库从word格式转换为JSON格式。 四种刷题模式包括顺序刷题、乱序刷题、错题模式和背题模式&#xff0c;可以根据自己的需求选择适合的模…

接口--抽象方法

回答问题&#xff1a; 1.接口是什么&#xff1f; 2.接口中可以包含什么内容&#xff1f; 3.如何定义接口格式&#xff1f; 4.接口定义抽象方法格式&#xff1f; Code //接口是公共规范标准&#xff0c;类似于“模具” //如何定义接口格式&#xff1f;/** public interface 接…

写在 Chappyz 即将上所之前:基于 AI 技术对 Web3 营销的重新定义

前不久&#xff0c;一个叫做 Chappyz 的项目&#xff0c;其生态代币 $CHAPZ 在 Seedify、Poolz、Decubate、ChainGPT、Dao Space 等几大 IDO 平台实现了上线后几秒售罄&#xff0c;并且 Bitget、Gate.io、PancakeSwap 等几大平台也纷纷表示支持&#xff0c;并都将在 11 月 13 日…

“目标值排列匹配“和“背包组合问题“的区别和leetcode例题详解

1 目标值排列匹配 1.1 从目标字符串的角度来看&#xff0c;LC139是一个排列问题&#xff0c;因为最终目标子串的各个字符的顺序是固定的&#xff1f; 当我们从目标字符串 s 的角度来看 LC139 “单词拆分” 问题&#xff0c;确实可以认为它涉及到排列的概念&#xff0c;但这种…

Android拖放startDragAndDrop拖拽Glide加载堆叠圆角图,Kotlin(5)

Android拖放startDragAndDrop拖拽Glide加载堆叠圆角图&#xff0c;Kotlin&#xff08;5&#xff09; import android.content.ClipData import android.graphics.Canvas import android.graphics.Point import android.os.Bundle import android.util.Log import android.view.…

单词规律问题

给定一种规律 pattern 和一个字符串 s &#xff0c;判断 s 是否遵循相同的规律。 这里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。 示例1: 输入: pattern “abba”, s “dog cat cat d…

macOS Sonoma 14.2beta2(23C5041e)发布(附黑白苹果镜像地址)

系统介绍 黑果魏叔11 月 10 日消息&#xff0c;今日向 Mac 电脑用户推送了 macOS 14.2 开发者预览版 Beta 2 更新&#xff08;内部版本号&#xff1a;23C5041e&#xff09;&#xff0c;本次更新距离上次发布隔了 14 天。 macOS Sonoma 14.2 添加了 Music 收藏夹播放列表&…

报时机器人的rasa shell执行流程分析

本文以报时机器人为载体&#xff0c;介绍了报时机器人的对话能力范围、配置文件功能和训练和运行命令&#xff0c;重点介绍了rasa shell命令启动后的程序执行过程。 一.报时机器人项目结构 1.对话能力范围 (1)能够识别欢迎语意图(greet)和拜拜意图(goodbye) (2)能够识别时间意…

AI 绘画 | Stable Diffusion 进阶 Embeddings(词嵌入)、LoRa(低秩适应模型)、Hypernetwork(超网络)

前言 Stable Diffusion web ui&#xff0c;除了依靠文生图&#xff08;即靠提示词生成图片&#xff09;&#xff0c;图生图&#xff08;即靠图片提示词生成图片&#xff09;外&#xff0c;这两种方式还不能满足我们所有的绘图需求&#xff0c;于是就有了 Embeddings&#xff0…

TiPro7000 Smart Tool V1.1无法打开解决办法

长江存储官网下载的TiPro7000 Smart Tool V1.1在win10运行时无法打开&#xff0c;转圈圈之后就没有反应了。官网下载的压缩包解压之后内容如下图。 解决办法&#xff1a;将.exe文件名的“致钛”二字删掉即可。文件名不能有中文。 打开后软件界面如下。 吐槽一下这软件做得挺简…

[Linux打怪升级之路]-信号的保存和递达

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、信号的保…