【C++】map set 底层刨析

文章目录

  • 1. 红黑树的迭代器
  • 2. 改造红黑树
  • 3. map 的模拟实现
  • 4. set 的模拟实现

在这里插入图片描述

在 C++ STL 库中,map 与 set 的底层为红黑树,那么在不写冗余代码的情况下使用红黑树同时实现 map 与 set 便是本文的重点。

1. 红黑树的迭代器

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以下问题:

  • begin()end()

    STL 明确规定,begin() 与 end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin() 可以放在红黑树中最小节点(即最左侧节点)的位置,end() 放在最大节点(最右侧节点)的下一个位置

    iterator begin()
    {
    	Node* subLeft = _root;
    	while (subLeft && subLeft->_left)
    	{
    		subLeft = subLeft->_left;
    	}
    	return iterator(subLeft);
    }
    
    const_iterator begin() const
    {
    	Node* subLeft = _root;
    	while (subLeft && subLeft->_left)
    	{
    		subLeft = subLeft->_left;
    	}
    	return const_iterator(subLeft);
    }
    
    iterator end()
    {
    	return iterator(nullptr);
    }
    
    const_iterator end() const
    {
    	return const_iterator(nullptr);
    }
    
  • operator++()operator--()

    Self& operator++()
    {
    	if (_node->_right)
    	{
    		// 右子树的中序第一个(最左节点)
    		Node* subLeft = _node->_right;
    		while (subLeft->_left)
    		{
    			subLeft = subLeft->_left;
    		}
    		_node = subLeft;
    	}
    	else
    	{
    		// 祖先里面孩子是父亲左的那个
    		Node* cur = _node;
    		Node* parent = cur->_parent;
    		while (parent && cur == parent->_right)
    		{
    			cur = parent;
    			parent = cur->_parent;
    		}
    		_node = parent;
    	}
    	return *this;
    }
    
    Self& operator--()
    {
    	// 跟++逻辑相反
    	return *this;
    }
    

2. 改造红黑树

#pragma once

enum Color
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Color _col;
	T _data;

	RBTreeNode(const T& data)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}
};

template<class T, class Ptr, class Ref>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ptr, Ref> Self;

	Node* _node;

	RBTreeIterator(Node* node)
		: _node(node)
	{}

	T& operator*()
	{
		return _node->_data;
	}

	T* operator->()
	{
		return &_node->_data;
	}

	Self& operator++()
	{
		if (_node->_right)
		{
			// 右子树的中序第一个(最左节点)
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		}
		else
		{
			// 祖先里面孩子是父亲左的那个
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator--()
	{
		// 跟++逻辑相反
		return *this;
	}

	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

	bool operator==(const Self& s)
	{
		return _node == s._node;
	}
};

// set->RBTree<K, K, SetKeyOfT>
// map->RBTree<K, pair<K, V>, MapKeyOfT>

// 因为关联式容器中存储的是<key, value>的键值对,因此
// K为key的类型
// T:如果是map,则为pair<K, V>;如果是set,则为K
// KeyOfT仿函数,取出T对象中的key
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
	typedef RBTreeIterator<T, T*, T&> iterator;
	typedef RBTreeIterator<T, const T*, const T&> const_iterator;

	iterator begin()
	{
		Node* subLeft = _root;
		while (subLeft && subLeft->_left)
		{
			subLeft = subLeft->_left;
		}
		return iterator(subLeft);
	}

	const_iterator begin() const
	{
		Node* subLeft = _root;
		while (subLeft && subLeft->_left)
		{
			subLeft = subLeft->_left;
		}
		return const_iterator(subLeft);
	}

	iterator end()
	{
		return iterator(nullptr);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

	iterator Find(const K& key)
	{
		KeyOfT kot;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return iterator(cur);
			}
		}
		return end();
	}

	pair<iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}

		KeyOfT kot;
		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;
		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;
			if (parent == grandfather->_left)
			{
				Node* 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)
					{
						//     g
						//   p   u
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//    g
						// p     u
						//   c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				// 情况一:叔叔存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				// 情况二:叔叔不存在或者存在且为黑
				else
				{
					// 旋转+变色
					//   g
					// u   p
					//       c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//    g
						// u     p
						//     c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);
	}

	void RotateL(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 RotateR(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;
		}
	}

private:
	Node* _root = nullptr;
};

3. map 的模拟实现

map 的底层结构就是红黑树,因此在 map 中直接封装一棵红黑树,然后将其接口包装下即可。

#pragma once

#include "RBTree.h"

namespace tjq
{
	template<class K, class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, const K, MapKeyOfT>::const_iterator const_iterator;
		
		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}

		const_iterator begin() const
		{
			return _t.begin();
		}

		const_iterator end() const
		{
			return _t.end();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};
}

4. set 的模拟实现

set 的底层为红黑树,因此只需在 set 内部封装一棵红黑树,即可将该容器实现出来。

#pragma once

#include "RBTree.h"

namespace tjq
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator;
		typedef typename RBTree<K, const K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

	private:
		RBTree<K, const K, SetKeyOfT> _t;
	};
}

END

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

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

相关文章

Day84:服务攻防-端口协议桌面应用QQWPS等RCEhydra口令猜解未授权检测

目录 端口协议-口令爆破&未授权 弱口令爆破 FTP&#xff1a;文件传输协议 RDP&#xff1a;Windows远程桌面协议 SSH&#xff1a;Linux安全外壳协议 未授权案例(rsync) 桌面应用-QQ&WPS&Clash QQ RCE 漏洞复现 WPS RCE 漏洞复现 Clas* RCE 漏洞复现 知识点…

90天玩转Python—04—基础知识篇:Python编程基础:标识符、保留字、注释、多行语句、print输出以及模块导入详解

90天玩转Python系列文章目录 90天玩转Python—01—基础知识篇:C站最全Python标准库总结 90天玩转Python--02--基础知识篇:初识Python与PyCharm 90天玩转Python—03—基础知识篇:Python和PyCharm(语言特点、学习方法、工具安装) 90天玩转Python—04—基础知识篇:Pytho…

淘宝里的优惠券在哪里查看_淘宝优惠券怎么找到领取

淘宝里的优惠券在哪里查看&#xff1f; 1、打开手机淘宝APP&#xff0c;点击右下角我的淘宝&#xff1b; 2、在我的淘宝里找到我的权益&#xff0c;看到优惠券后点击进入&#xff1b; 3、我淘宝我的权益券里可以看到已领取到的淘宝天猫优惠券&#xff1b; 淘宝优惠券怎么找到领…

开源代码分享(17)-基于足球队训练算法(Football Team Training Algorithm,FTTA)的组合风速预测

参考文献&#xff1a; [1]Tian Z, Gai M. Football team training algorithm: A novel sport-inspired meta-heuristic optimization algorithm for global optimization[J]. Expert Systems with Applications, 2024, 245: 123088. 1.算法基本原理 足球队训练算法&#xff0…

(学习日记)2024.04.01:UCOSIII第二十九节:消息队列实验(待续)

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

独角数卡对接码支付收款教程

1、到码支付后台找到支付配置。2、将上面的复制依次填入&#xff0c;具体看下图&#xff0c;随后点立即添加 商户ID商户PID 商户KEY异步不能为空 商户密钥商户密钥

jenkins_Pipeline使用测试

jenkins—Pipeline使用测试 安装jenkins # jar包启动 https://sg.mirror.servanamanaged.com/jenkins/war-stable/2.346.1/jenkins.war https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz [rootvm ~]# tail /etc/profile ... export JAVA_HOME/opt…

51单片机入门:LED点阵屏

LED点阵屏介绍 LED点阵屏由若干个独立的LED组成&#xff0c;LED以矩阵的形式排列&#xff0c;以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种场合&#xff0c;如&#xff1a;广告屏、公告牌等。 分类&#xff1a; 按颜色&#xff1a;单色、双色、全彩&#x…

GPT4解除限制使用次数了!GPT5预计要推出了!

今天登录GPT Plus的时候&#xff0c;出现了如下提示&#xff1a; With DALLE,browing and analysis Usage limits may apply GPT4已经没有了数量和时间限制的提示。 更改前&#xff1a;每 3 小时限制 40 次&#xff08;团队计划为 100 次&#xff09;&#xff1b;更改后&#…

尚硅谷html5+css3(1)

1.基本标签&#xff1a; <h1>最大的标题字号 <h2>二号标题字号 <p>换行 2.根标签<html> 包括<head>和<body> <html><head><title>title</title><body>body</body></head> </html> 3…

C语言-预定义符号

编译和链接&#xff08;基础速通版&#xff09;-CSDN博客https://blog.csdn.net/Jason_from_China/article/details/137182220 预定义符号 包含 C语⾔设置了⼀些预定义符号&#xff0c;可以直接使⽤&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的…

二叉树中所有距离为k的节点

题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 从目标节点的左孩子&#xff0c;右孩子&#xff0c;父亲节点出发去找&#xff0c;左孩子 右孩子 做法简单 &#xff0c; 主要是父亲节点 &#xff0c;因此我们需要知道每个节点的父亲节点&am…

我的C++奇迹之旅:内联函数和auto关键推导和指针空值

文章目录 &#x1f4dd;内联函数&#x1f320; 查看内联函数inline方式&#x1f309;内联函数特性&#x1f309;面试题 &#x1f320;auto关键字(C11)&#x1f320; auto的使用细则&#x1f309;auto不能推导的场景 &#x1f320;基于范围的for循环(C11)&#x1f320;范围for的…

测距神器——无影无踪的超声波!

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海&#xff0c;原创文章欢迎点赞分享&#xff01; 1880年居里兄弟发现&#xff0c;在石英晶体的特定方向上施…

AI Agent在芯片设计领域的未来应用

文章目录 (一)首先是“AI Agent”的Agent怎么理解(二)其次是“AI Agent”的AI怎么理解1) 联邦学习/密文计算/密码学算法/MPC2) sklearn 库所有算法的MPC化实现密文计算安全改造和性能优化3 )NLP Bert 推荐召回(三)最后,“AI Agent”+芯片设计,怎么理解认识一个新概念…

解压缩软件哪个好用 Mac免费解压软件哪个好 解压软件推荐 beeterzip免费下载

解压缩软件在Mac办公中是必不可少的&#xff0c;不仅能够节省时间和内存&#xff0c;更能提升传输效率。虽然Mac自带的解压缩软件归档实用工具可以对zip文件进行解压&#xff0c;但是对于他格式文件就无能为力了。 因此&#xff0c;想要满足多类型文件解压缩需求&#xff0c;可…

基于vue+node.js导师选择分配管理系统

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode .设计一套导师选择管理系统&#xff0c;帮助学校进行导师选择管理等繁琐又重复的工作&#xff0c;提高工作效率的同时&#xff0c…

多线程-相关概念

程序、进程与线程 程序&#xff08;program&#xff09;&#xff1a;为完成特定任务&#xff0c;用某种语言编写的一组指令的集合。即指一段静态的代码&#xff0c;静态对象。 进程&#xff08;process&#xff09;&#xff1a;程序的一次执行过程&#xff0c;或是正在内存中运…

linux文件权限与数字转化

chmod命令——change mode&#xff0c;可以对特定文件文件夹权限进行更改 这里我们看到&#xff0c;当执行了chmod u-x try.sh后&#xff0c;try文件底色变为白色&#xff0c;即为其执行权限被“减去” 在linux系统中&#xff0c;权限的减去是通过权限的数字表示来实现的&#…

【可靠性】陷阱电荷对TDDB影响的多尺度模拟

【From Accelerated to Operating Conditions: How Trapped Charge Impacts on TDDB in SiO2 and HfO2 Stacks】 文章总结&#xff1a; 本研究深入探讨了在SiO2和HfO2介质堆叠中&#xff0c;陷阱电荷对时间依赖介电击穿&#xff08;TDDB&#xff09;现象的影响。通过引入载流子…