数据结构:搜索二叉树 | 红黑树 | 验证是否为红黑树

news2024/11/19 0:36:17

文章目录

        • 1.红黑树的概述
        • 2.红黑树的性质
        • 3.红黑树的代码实现
          • 3.1.红黑树的节点定义
          • 3.2.红黑树的插入操作
          • 3.3.红黑树是否平衡

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

1.红黑树的概述

平衡二叉树要求左右子树的高度差的绝对值不超过1,所以平衡二叉树是高度平衡的,正是因为它要求严格控制高度差,在频繁的插入的删除的时候导致旋转次数过多带来了一定的性能消耗!

红黑树也是一颗特殊的搜索二叉树,任何一条从根到叶子的路径上各个结点由颜色(红黑)方式的限制,红黑树确保没有一条路径会比其他路径长出两倍(最长路径不会超过最短路径的两倍),它而是接近平衡的。红黑树没有严格要求高度的平衡,所以红黑树在总体的性能上会略优于平衡二叉树(AVL树)。

在现实的各种应用中都是使用红黑树的来充当数据结构,如:Java集合中的TreeMap,C++STL中Set、Map等都是使用红黑树而不是AVL树。可能在查找方面AVL树会由于红黑树,但是几十次的常数差别,在现代CPU(大概每秒几十亿次)来说完全不受影响,AVL树和红黑树查找的时间复杂度都是在一个等级上O(log_2(N)),红黑树在插入和删除会优于AVL树!

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

在这里插入图片描述

关于路径问题

上图有11条路径

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

在这里插入图片描述

最短路径:全黑的路径 最长路径:一黑一红相间的路径

上面的图情况就是最大的(4个节点/2个节点 = 2倍)的情况!

3.红黑树的代码实现
3.1.红黑树的节点定义
enum color
{
    RED,
    BLACK
};

template<class K, class V>
struct RBNode
{
    std::pair<K, V> _kv;

    RBNode<K, V>* _left;
    RBNode<K, V>* _right;
    RBNode<K, V>* _parent;
    color _color;

    RBNode(const std::pair<K, V> kv)
        :_kv(kv)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _color(RED)
    {}
};
3.2.红黑树的插入操作

第一大类情况:cur、parent为红,grandfather为黑,uncle存在且为红!

在这里插入图片描述

  1. 将parent和uncle都变为黑,grandfather变为红
  2. 但是grandfather,如果是根节点直接将其设为黑即可,
  3. 如果grandfather不为红,如下图:则将cur = grandfather继续重复步骤1和步骤2

在这里插入图片描述

**第二大类情况:**cur为红,parent为红,grandfather为黑,uncle不存在或uncle存在且为黑

单旋的情况:

  1. 下图是uncle不存在或uncle存在且为黑单旋的情况,通过节点的位置判断单旋:如条件是grandfather ->left = parent; parent->left = cur; 直线说明是单旋,都是左边说明右边高,要右单旋!
  2. 变色,grandfather变为红,parent变为黑色;可以看到,调整后的根据原来的grandfather是黑色,无论往上的祖先是何种颜色,都不会出现两个红色连一起的情况,不用往上调整了!

在这里插入图片描述

双旋的情况:

  1. 通过上面的例子可以知道,存在uncle且为黑和不存在uncle的处理结果是一样的!
  2. 通过下图的判断,是折线:grandfather ->left = parnt;parent->right =cur;通过平衡二叉树的插入操作的学习,可以知道,这种情况先左单旋,再右单旋!
  3. 变色:grandfather变为红,cur变为黑;可以看到,调整后的根据原来的grandfather是黑色,无论往上的祖先是何种颜色,都不会出现两个红色连一起的情况,不用往上调整了!

在这里插入图片描述

上面就是红黑树的**左子树的插入操作!右子树的插入99%是一样的!**可能没有99%,哈哈哈!但是是类似的操作!简单画个图:

第一大类情况

在这里插入图片描述

第二大类情况

单旋情况:

在这里插入图片描述

双旋情况:

在这里插入图片描述

bool insert(const std::pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_color = 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);

	if (parent->_kv.first < kv.first)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}

	cur->_parent = parent;

	while (parent && parent->_color == RED)
	{
		Node* grandfather = parent->_parent;

		// parent是左子树
		if (parent == grandfather->_left)
		{
			
			Node* uncle = grandfather->_right;
			// 第一大类:uncle存在且为红
			if (uncle && uncle->_color == RED)
			{
				// 改变颜色
				parent->_color = uncle->_color = BLACK;
				grandfather->_color = RED;

				// 修改条件,继续往上执行
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 第二大类:uncle不存在或uncle存在且为黑
			{
				// 单旋情况
				if (cur == parent->_left)
				{
					// 右单旋
					RotateRight(grandfather);

					// 改变颜色
					parent->_color = BLACK;
					grandfather->_color = RED;
				}
				else  // 双旋情况
				{
					RotateLeft(parent);
					RotateRight(grandfather);

					cur->_color = BLACK;
					grandfather->_color = RED;
				}

				// 这种情况就不用重复调整,直接跳出循环
				break;
			}
		}
		else // parent是右子树
		{
			Node* uncle = grandfather->_left;

			if (uncle && uncle->_color == RED)
			{
				parent->_color = uncle->_color= BLACK;
				grandfather->_color = RED;

				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					RotateLeft(grandfather);

					parent->_color = BLACK;
					grandfather->_color = RED;
				}
				else
				{
					RotateRight(parent);
					RotateLeft(grandfather);

					cur->_color = BLACK;
					grandfather->_color = RED;
				}
				break;
				
			}
			
		}
	}
	// 根绝对为黑色
	_root->_color = BLACK;

	return true;
}
3.3.红黑树是否平衡

根据每条路径的黑节点的个数相同判断!

bool isBanlan()
{
    // 如何判断是否为红黑树
    // 1.高度行不行? 不行! 2.能不能根据节点的个数不操作2倍的关系? 也不行
    // 3.那有什么是确定的?所有路径黑节点的个数相等!根据这个性质!
    Node* cur = _root;

    int checkNum = 0;
    while (cur)
    {
        if(cur->_color == BLACK)
            checkNum++;

        cur = cur->_left;
    }
  
    return isBanlan(_root,checkNum,0); 
}

bool isBanlan(Node* root,int &checkNum,int blackNum)
{
	if (root == nullptr)
		return true;

	if (root->_color == BLACK)
	{
		blackNum++;
	}

	if (root->_left == nullptr && root->_right == nullptr)
	{
		return blackNum == checkNum ? true : false;
	}
	bool left = isBanlan(root->_left, checkNum, blackNum);
	bool right = isBanlan(root->_right, checkNum, blackNum);
	
	return left && right;
}

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

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

相关文章

【STM32CubeMX串口通信详解】USART2 -- DMA发送 + DMA空闲中断 接收不定长数据

&#xff08; 本篇正在编写、更新状态中.....) 文章目录&#xff1a; 前言 前言 本篇&#xff0c;详细地用截图解释 CubeMX 对 USART2 的配置&#xff0c;HAL函数使用&#xff0c;和收发程序的编写。 收、发机制&#xff1a;DMA发送 DAM空闲中断接收。 DMA空…

物业app开发,提升社区管理效率

随着城市化进程的加速&#xff0c;小区和社区管理面临着越来越多的挑战。传统的管理模式已经无法满足高效管理的需求&#xff0c;而物业app的应用正逐渐成为解决问题的最佳选择。通过物业app&#xff0c;管理人员可以更好地管理小区设施、维护房屋&#xff0c;居民们也能够更便…

pytestallure分析redis的数据并动态生成testCase报告

1.pytest.mark.parametrize pytest.mark.parametrize 是一个pytest的装饰器&#xff0c;它可以用于将参数传递给测试函数。使用 pytest.mark.parametrize 装饰器时&#xff0c;需要在装饰器中指定参数名称和参数值。对于多个参数&#xff0c;可以使用多个装饰器。 下面是一些…

【网络安全】常见的网络威胁有哪些?

随着互联网的快速发展&#xff0c;网络安全问题日益凸显。常见的网络威胁包括病毒、木马、恶意软件等。这些威胁不仅会影响计算机的安全运行&#xff0c;还会窃取用户的个人信息&#xff0c;造成巨大的损失。因此&#xff0c;我们需要采取一些措施来保护自己的网络安全。 常见的…

HarmonyOS鸿蒙应用开发( 四、重磅组件List列表组件使用详解)

List列表组件&#xff0c;是一个非常常用的组件。可以说在一个应用中&#xff0c;它的身影无处不在。它包含一系列相同宽度的列表项&#xff0c;适合连续、多行呈现同类数据&#xff0c;如商品列表、图片列表和和文本列表等。ArkUI 框架采用 List 容器组件创建列表&#xff08;…

RFID标签是什么?该技术有哪些应用领域?

射频识别&#xff08;RFID&#xff09;技术利用电磁场&#xff0c;自动识别和跟踪附在物体上的标签&#xff0c;其中&#xff0c;近场通信&#xff08;NFC&#xff09;是一种基于短距离RFID高频技术的标准&#xff0c;支持13.56 MHz的频率。 NFC技术在现今的产品中应用广泛&am…

智能GPT图书管理系统(SpringBoot2+Vue2)、接入GPT接口,支持AI智能图书馆

☀️技术栈介绍 ☃️前端主要技术栈 技术作用版本Vue提供前端交互2.6.14Vue-Router路由式编程导航3.5.1Element-UI模块组件库&#xff0c;绘制界面2.4.5Axios发送ajax请求给后端请求数据1.2.1core-js兼容性更强&#xff0c;浏览器适配3.8.3swiper轮播图插件&#xff08;快速实…

Ubuntu findfont: Font family ‘SimHei‘ not found.

matplotlib中文乱码显示 当我们遇到这样奇怪的问题时, 结果往往很搞笑 尝试1不行 Stopping Jupyter Installing font-manager: sudo apt install font-manager Cleaning the matplotlib cache directory: rm ~/.cache/matplotlib -fr Restarting Jupyter. 尝试2 This work fo…

【机组】计算机组成原理实验指导书.

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 第一章 性能特点 1.1 系…

(音乐软件)Spotify声破天8.9.6.458

【应用名称】&#xff1a;Spotify-声破天 【适用平台】&#xff1a;#Android 【软件标签】&#xff1a;#Spotify 【应用版本】&#xff1a;8.9.4 → 8.9.6 【应用大小】&#xff1a;67MB 【软件说明】&#xff1a;软件升级更新。iOS可配合qx小火箭类的工具对该软件进行解锁…

vue(vue2)使用svg格式图标

先安装插件 配置svg文件夹&#xff0c;新建icons文件&#xff0c;svg文件夹放svg后缀文件 index.js文件中的配置 import Vue from "vue" import svgIcon from "/common/iconSvg/index.vue"Vue.component(svg-icon,svgIcon) //挂载全局组件//下面…

雪洁宠物领养管理系统的设计与实现-计算机毕业设计源码58331

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

Qt解析含颜色的QString字符串显示到控件

1、需求 开发接收含颜色字符串显示到窗口&#xff0c;可解析字符串颜色配置窗口属性&#xff0c;且分割字符串显示。 mprintf(“xxxxxx”)&#xff1b;打印的xxxxxx含有颜色配置。 2、实现方法 2.1、条件 选用Qt的PlainTextEdit控件显示字符串&#xff0c;配置为只读模式 …

存内计算引领新一代技术革新,开启算力新时代

文章目录 存内计算与传统计算的区别 存内计算与传统计算的区别 存内计算芯片的优势 存内计算在各个领域的应用 存内计算技术对未来发展的影响 CSDN存内计算开发者社区&#xff1a;引领新一代技术革新的最前沿 社区内容专业度 社区具备的资源 社区的开放性 社区招募令…

网络上的诈骗:了解网络钓鱼

网络钓鱼&#xff08;Phishing&#xff09;是一种常见的网络诈骗形式&#xff0c;旨在通过假冒合法的电子通讯手段骗取个人信息、财务数据或登录凭证。 这种攻击通常涉及发送看似来自可信来源的电子邮件、短信或社交媒体消息&#xff0c;诱使受害者提供敏感信息或点击恶意链接…

前端项目部署发版流程

一、本地代码以全部提交并推送至仓库 二、项目根目录添加&#xff08;Dockerfile、nginx.conf文件&#xff09; 三、npm run build打包生成dist文件&#xff08;项目根目录&#xff09; 四、启动docker(登录状态) 五、执行命令 docker buildx build -t ‘项目的容器集群地…

【产品交互】超全面B端设计规范总结

不知不觉已经深耕在B端这个领域3年有余&#xff0c;很多人接触过B端后会觉得乏味&#xff0c;因为B端的设计在视觉上并没有C端那么有冲击力&#xff0c;更多的是结合业务逻辑&#xff0c;设计出符合业务需求的交互&#xff0c;以及界面排版的合理性&#xff0c;达到产品的可用性…

国产芯片替代趋势:发展前景与挑战全面解读

在当今数字化浪潮中&#xff0c;国产芯片替代正成为引人注目的趋势。本文将深入剖析该趋势的发展前景与挑战&#xff0c;并提供替代芯片查询的实用技巧。无论是科技从业者普通用户&#xff0c;都将在这篇文章中找到了对未来技术格局的洞察。同时&#xff0c;我们将通过上道合顺…

气象条件对铸铁平台地基深度有哪些影响呢——河北北重

气象条件对铸铁平台地基有以下影响&#xff1a; . 1.地震 地震可能导致地基的震动和错动&#xff0c;因此地震活跃区域的建筑物通常需要更深的地基以提供更大的稳定性。 2..温度变化&#xff1a;气温的变化会导致地基中的土壤膨胀和收缩&#xff0c;从而影响地基的稳定性。特…

《WebKit 技术内幕》学习之五(4): HTML解释器和DOM 模型

4 影子&#xff08;Shadow&#xff09;DOM 影子 DOM 是一个新东西&#xff0c;主要解决了一个文档中可能需要大量交互的多个 DOM 树建立和维护各自的功能边界的问题。 4.1 什么是影子 DOM 当开发这样一个用户界面的控件——这个控件可能由一些 HTML 的标签元素…