红黑树的迭代器红黑树与AVL树的比较

news2024/11/18 10:41:38

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

文章目录

  • 一、红黑树泛型实现map,set
    • 对多出来的模板参数的解释
  • 二、map和set对红黑树迭代器的封装
    • ①迭代器operator++
    • ②operator--
  • 三、红黑树改装后的insert代码
  • 四、红黑树与AVL树比较

一、红黑树泛型实现map,set

struct RBTreeNode
{
	RBTreeNode<K,V>*_left;
	RBTreeNode<K,V>*_right;
	RBTreeNode<K,V>*_parent;
	T _data;//int pair<K,V>
	Color _col;
	RBTreeNode(const T&data)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(data)
	{}
};
template<class K,class T,class KeyOfT>
//本来要T就足够了,但是Find需要使用K,所以这个参数模板K用于专用于Find
class RBTree
{
	typedef RBTreeNode<T> Node;
private:
	Node*root;
}

模板参数的第一个专用于Find,下面细说

template<class K,class V>
class map
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<K,V>&kv)
		{
			return kv.first;
		}	
	}
private:
	RBTree<K,pair<K,V>,MapKeyOfT> _t;
}

template<class K>
class set
{
	struct SetKeyOfT
	{
		const K& operator()(const K&key)
		{
			return key;
		}	
	}
private:
	RBTree<K,K,SetKeyOfT> _t;
}

map和set只是薄薄的一层封装,真正还是红黑树实现的,这颗红黑树的DataType,你传什么我就是什么,我拿一个T来接收这个类型
在这里插入图片描述

对多出来的模板参数的解释

①****首先我们只需要给红黑树传T就行了,不然的话一个红黑树就需要实现两次,分别是K的树和pair<K,V>的树,因为有两个模板参数

②****其次我们既然有一个T就行了,那我们又在前面传一个K过去干嘛?其实在其他地方好像确实用不着,但是注意find的时候,我们都是通过键值查找的,不可能说T _t; 我通过_t.first来查找它的value吧,那这个结点的类型你知道是pair<K,V>还是K呢?所以我们需要单独的传K来解决find这个问题

③红黑树其实是有比较的,因为红黑树是一颗搜索二叉树,那么中序遍历就是一个有序序列,所以我们在插入结点的时候就需要通过比较来确定插入结点的位置,所以这时候又要多一个KeyOfT的一个参数,因为和②相同,我们不确定结点的类型,不能通过_t.first来比较。另外呢KeyOfT就是一个仿函数,我们实现红黑树 的时候就可以KeyOfT(cur->_data)来对结点的类型进行处理。

可以看到,STL源码中也是通过该种方式进行处理的
在这里插入图片描述

二、map和set对红黑树迭代器的封装

template<K,V>
class Map
{
	typedef typename RBTree<K,pair<K,V>,MapKeyOfT>::iterator iterator;
	//typedef,加上typename的原因是区分这里的迭代器和静态成员变量
	//因为静态成员变量也是这样调用的,编译器分不清
	iterator begin()
	{
		_t.begin();
	}
	iterator end()
	{
		_t.end();
	}
};//复用红黑树的操作

①迭代器operator++

这里就需要非递归的思路,但是并不能直接用栈去模拟,因为直接用栈的话太大了,我们可以放一个结点的指针
在这里插入图片描述

如果右子树不为空:那么找右子树中序遍历的第一个结点(也就是右子树最左结点)
如果右子树为空
①如果该结点是左结点,那么找父亲
②如果该结点是右结点,那么找父亲的父亲,直到找到某个结点是左孩子(因为右孩子的父亲都被访问过,这里需要对二叉树的非递归非常了解)

/*模板*/Self&operator++()
{
	if(_node->_right)//有右孩子,找右孩子中序遍历 第一
	{
		Node*left = _node->_right;
		while(left -> _left)
		{
			left=left->_left;	
		}
		_node = left;
	}
	else
	{
		Node*parent=_node->_parent;
		Node*cur = _node;
		while(parent!=nullptr && ncur==parent->_right)
		{
			cur = cur->parent
			parent=parent->_parent;
			//直到为左节点或者为根
		}
		_node=parent;
		//返回父节点
	}
	return *this;
}

②operator–

在这里插入图片描述
5 6 7 8 10 11 12 13 15
15 13 12 11 10 8 7 6 5
和++完全反过来即可

Self&operator--()
{
	if(_node->_left)
	//有左孩子,访问左子树反向中序的第一个,也就是最右
	{
		Node*right = _node->_left;
		while(right->_right)
		{
			right = right->_right;
		}	
		_node=right;
	}		
	else
	//右孩子,访问父节点
	//左孩子,继续往上,直到是右孩子,或者是根,返回父亲
	{
		Node*parent=_node->_parent;
		Node*cur = _node;
		while(parent!=nullpre&&cur==parent->_left)
		{
			cur=cur->_parent;
			parent=parent->_parent;
		}
		_node=parent;
	}
	return *this;
}

但是存在一个问题就是当迭代器是s.end()的时候,相当于it是nullptr,没办法–,所以库中用了其他方法来解决,如下图
在这里插入图片描述

三、红黑树改装后的insert代码

pair<iterator,bool> Insert(const T&data)
{
	KetOfT kot;//仿函数对象
	if(_root==nullptr)
	{
		_root=new Node(data);
		_root->_col=BLACK;
		return make_pair(iterator(_root)/*iterator类构造*/,true);
	}
	Node*parent=nullptr;
	Node*cur=_root;
	while(cur)//找到插入位置
	{
		if(kot(cur->_data)<kot(data))
		{
			parent=cur;
			cur=cur->_right;
		}
		else if(kot(cur->_data)>kot(data))
		{
			parent=cur;
			cur=cur->left;
		}
		else
		{
			return make_pair(iterator(cur),false);//已存在
		}
	}
	//已找到位置,在此处插入
	cur=new Node(data);
	Node*newnode = cur;//记录返回的迭代器位置
	cur->col=RED;
	//看插在左还是右
	if(kot(parent->_data)<kot(data))
	{
		parent->right=cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur->parent=parent;
	//插入了之后进行平衡已经颜色校正
	//也就是如果不平衡则 红黑树三种处理情况
	while(parent&&parent->_col==RED)//需要处理
	{
		Node*grandfather= parent->_parent;
		assert(grandfather&&grandfather->col==BLACK);
		//存在并且爷爷的col不可能是红
		Node*uncle;//红黑树一切看叔叔
		if(parent==grandfather->_left)
		{
			uncle = grandfather->_right;
			if(uncle&&uncle->_col==RED)
			//情况一,叔叔为红,往上变色即可
			{
				parent->_col = uncle->_col=BLACK;
				grandfather->_col = RED;
				cur=grandfather;
				parent=cur->_parent;
			}	
			else
			//情况二三、叔叔存在且为黑或者不存在,需要翻转
			{
				if(cur==parent->_left)
				//左左,需要一次翻转,然后根据情况图进行变色
				{
					RotateR(grandfather);
					grandfather->_col=RED;
					parent->_col=BLACK;
					//对照翻转后的图改颜色
				}
				else if(cur==parent->_right)
				//左右,单次翻转的话会没有效果,双旋转+变色
				{
					RotateL(parent);
					RotateR(grandfather);
					cur->_col=BLACK;
					grandfather->_col=RED;	
				}
			}	
		}
		else  if(parent==grandfather->_right)
		{
			//差不多的,我就不写了
		}		
	}
	_root->_col=BLACK;
	return make_pair(iterator(newnode),true);
}

四、红黑树与AVL树比较

红黑树和AVL树都是高效的平衡搜索二叉树,增删查改的时间复杂度都是O(logN),因为最坏查找高度次,红黑树不追求绝对平衡,其次只需要保证最长路径不超过最短路径的二倍(通过颜色的互斥来达到这一点),这就决定了红黑树会有更少的旋转,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以红黑树的运用比AVL树更多

在这里插入图片描述

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

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

相关文章

Web3中文|全球首个中华武术收藏级卡牌系列发布,传武文化的未来在元宇宙?

谈及中华武术的传承与发展&#xff0c;大家首先能想到什么&#xff1f;小说、电影、动画、游戏……等等&#xff0c;都是曾经的载体。作为中华文化极其重要的一部分&#xff0c;武术是国人独有的标签&#xff0c;太多经典作品珠玉在前&#xff0c;如今武术的传承&#xff0c;需…

【JavaScript】跟着pink学习第二天部分案例

1.猜数字游戏 三次机会&#xff0c;猜1~50之间的一个整数 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vi…

12月第4周榜单丨B站UP主排行榜(飞瓜数据B站)发布!

飞瓜轻数发布2022年12月19日-12月25日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营能力强的…

【Python百日进阶-数据分析】Day140 - plotly表:plotly.graph_objects.Table()

文章目录一、语法二、参数三、返回值四、实例4.1 基本表4.2 样式表4.3 使用 Pandas 数据框4.4 Dash中的表4.5 更改行和列大小4.6 交替行颜色4.7 基于变量的行颜色4.8 基于变量的单元格颜色一、语法 构造一个新的 Table 对象 用于查看详细数据的表格视图。数据以行和列的网格排…

screen命令简要说明

screen命令简要说明 screen命令有什么用 screen命令虚拟了一个终端(session)&#xff0c;可在终端内运行命令&#xff0c;多次运行screen命令可以虚拟多个不同的终端&#xff1b; 每个session可以开启多个窗口&#xff0c;每个窗口有自己的shell&#xff0c;可以在不同的窗口…

ConcurrentSkipListMap-跳跃表 源码解析

ConcurrentSkipListMap-跳跃表 源码解析 问题 跳跃表长什么样子呢&#xff1f;跳跃表如何查找指定 key 数据呢&#xff1f;跳跃表如何添加指定 key-value 数据呢&#xff1f;跳跃表如何删除指定 key 数据呢&#xff1f; 理论知识 跳表是一个随机化的数据结构&#xff0c;实…

如何将两个笔记本电脑进行相互投屏

文章目录&#xff09;使用以下定义&#xff1a;实际中&#xff1a;A和B电脑分别安装无线显示器B电脑这么做&#xff1a;A电脑这么做&#xff1a;使用以下定义&#xff1a; 投影的电脑为&#xff1a;A电脑 待投影的电脑为&#xff1a;B电脑 实际中&#xff1a; A电脑为win10系…

《HelloGitHub》第 81 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01;简介HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。https://github.com/521xueweihan/HelloGitHub这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Pyth…

研讨会回顾 | 自动化测试“领导者”SmartBear解析软件质量与测试现状调研

2022年12月6日&#xff0c;龙智与软件测试自动化“领导者”SmartBear联合举办了主题为“ 如何通过自动化测试实现降本、增效与提质”的在线研讨会。 会上&#xff0c;SmartBear亚太渠道经理何平康深入解读了《2022年SmartBear软件质量与测试报告》&#xff0c;并从全球质量测试…

java开源工作流的特点介绍

我们都知道&#xff0c;在业务量激增的情况下&#xff0c;采用工作效率高的低代码开发平台已然成为趋势。目前&#xff0c;有不少专业的服务商正在积极研发低代码开发平台&#xff0c;立志为客户朋友提升办公协作效率而提升研发进度。本文将为大家介绍java开源工作流的特点&…

鹏孚隆冲刺创业板上市:计划募资约7亿元,部分收入来自海外

12月27日&#xff0c;北京易诚互动网络技术股份有限公司&#xff08;下称“易诚互动”&#xff09;在深圳证券交易所更新招股书&#xff0c;准备在创业板上市。本次冲刺上市&#xff0c;易诚互动计划募资3.13亿元&#xff0c;将用于用于数字银行应用平台升级项目、大数据智能风…

Spring Cloud(十七):高并发设计

秒杀 秒杀业务初步分析秒杀系统的挑战秒杀系统设计通用秒杀架构 页面访问常见的秒杀系统架构商城的秒杀系统设计和实现秒杀的隔离业务隔离系统隔离数据隔离 实际部署 OpenResty商品获取库存获取 Lua 访问Redis从库 — Linux 进程间通信IPC&#xff08;管道、匿名管道、共享内…

ArcGIS中ArcMap分割栅格Split Raster工具没有结果的解决

本文介绍在ArcMap软件中&#xff0c;进行分割栅格&#xff08;Split Raster&#xff09;工具处理后&#xff0c;得不到结果文件的解决方法。 最近&#xff0c;需要基于一个面要素类&#xff0c;对一个栅格遥感影像加以分割。如下图所示&#xff0c;这个面要素类中有3个部分&…

维视智造斩获2022年度光能杯最具影响力“智造”企业奖

近日&#xff0c;由光伏行业权威媒体和机构——索比光伏网、索比咨询联合主办的2022年度“光能杯”影响力大奖榜单发布&#xff0c;维视智造凭借硬件与AI算法能力、凭借在光伏行业具有创新性的智能制造产品方案与落地的标杆案例&#xff0c;斩获“2022年最具影响力“智造”企业…

算法学习:第一天-------位运算

前言 位运算是在算法设计中的一种非常重要和高效的方法&#xff0c;常见的有与运算&#xff0c;非运算&#xff0c;异或运算。我们常用的比较多的可能就是异或运算&#xff0c;又叫无进位相加。 1.1 取非运算----&#xff08;~&#xff09; 取非运算其实就是和我们的无符号数…

LCR测试仪的测试原理及使用方法

LCR表是电子测量仪器中经常使用的电子仪器&#xff0c;作为电子仪器的一员&#xff0c;LCR表经常出现在高校实验室和研究所里&#xff0c;但是对于刚接触它的用户来说&#xff0c;可能还不太了解。今天安泰测试就给大家介绍一下LCR表的测试原理和使用方法。 LCR的含义&#xf…

Crack:GrapeCity Documents for Excel 6.0.1

v6 中 GrapeCity Documents for Excel 的新增功能 ocuments for Excel (GcExcel) v6 版本现已上线&#xff01;该版本引入了新的基于 JavaScript 的数据查看器控件、功能和对 GcExcel .NET和Java API 的模板增强&#xff0c;以及更多与SpreadJS兼容的功能。看看下面的主要亮点。…

第07讲:Redis集群之cluster

一、什么是Cluster&#xff1f; redis的哨兵模式基本已经可以实现高可用&#xff0c;读写分离 &#xff0c;但是在这种模式下每台redis服务器都存储相同的数据&#xff0c;很浪费内存&#xff0c;所以在redis3.0上加入了cluster模式&#xff0c;实现的redis的分布式存储&#x…

pycharm运行显示未安装包,但其实已经安装

问题描述 在下载了专业版的pycharm后&#xff0c;在pycharm终端运行项目的时候&#xff0c;无论什么包&#xff0c;只要你下载&#xff0c;就是显示已经存在&#xff08;当然存在了&#xff0c;我虚拟环境之前都下的有&#xff09; REquirement already statisfied&#xff1a;…

【OpenCV-Python】教程:9-1 级联分类器

OpenCV Python 级联分类器 【目标】 Haar 级联目标检测器工作方式&#xff1b;Haar 级联分类器检测人脸和人眼 【理论】 基于Haar特征的级联分类器的目标检测是Paul Viola和Michael Jones在2001年的论文中提出的一种有效的目标检测方法。这是一种基于机器学习的方法&#x…