【C++】map,set简单操作的封装实现(利用红黑树)

news2025/2/23 11:37:28

文章目录

  • 一、STL中set与map的源码
  • 二、 红黑树结点的意义
  • 三、仿函数的妙用
  • 四、set,map定义迭代器的区别
  • 五、map,set迭代器的基本操作:
    • 1.begin() end()
    • 2.operator++
    • 3.operator--
  • 六、迭代器拷贝构造特殊处理
  • 7.源码
    • RBTree.h
    • 2.MyMap.h
    • 3.MySet.h


一、STL中set与map的源码

在这里插入图片描述

在这里插入图片描述

因为关联式容器中存储的是<key, value>的键值对,因此k为key的类型,
ValueType: 如果是map,则为pair<K, V>; 如果是set,则为k
KeyOfValue: 通过value来获取key的一个仿函数类

二、 红黑树结点的意义

我们知道map,和set需要用红黑树来实现,但我们map的数据类型是键值对pair<K,V>类型,key的数据类型是单纯的K类型,那如何写出一个通用的红黑树模板呢?

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),
		_col(RED),
		_data(data)
	{}
};

我们这里把pair<K,V>看成一个整体,我们设计模板的时候就不需要考虑是不是键值对类型,需不需要多传一个模板参数的问题,达到了普适性。

在map中,T传pair<K,V>类型
在set中,T传K类型

三、仿函数的妙用

我们value_type类型用模板参数T代替之后,这个时候就会衍生一个问题,我T可能为键值对类型,我键值对之间怎么比较呢?
例如:T t1与T t2两个变量,我们肯定不能直接比较,肯定要依据他们的键值大小进行比较,所以我们需要自己写一个用于比较的函数,这个时候仿函数刚好能发挥这个用处,可以作为模板参数传入自己写的比较函数

取出他们的键,让他们进行比较,这里set也这样写是为了配合map,因为两者都用的一个红黑树模板

struct SetKeyOfT {
			const K& operator()(const K&key) {
				return key;
			}
		};


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

示例:红黑树中Find函数的实现:

Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;//KeyOfT为仿函数的类型
		//写好仿函数后先实例化出来
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

四、set,map定义迭代器的区别

因为set插入进去后,set的值不可以被修改,为了实现这一操作我们可以在迭代器上下手

//typename是告诉编译器这里后面跟的是类型不是对象,以免编译器报错
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
//既然不可修改,那我就都用const类型的迭代器

在map中,我们是键不可修改,而其所对应的值可被修改,所以不能用set的那种操作,可以在传模板参数的时候动手脚,传pair的时候直接把K改为const类型

typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

五、map,set迭代器的基本操作:

1.begin() end()

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

		return iterator(leftMin);
	}

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

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

		return const_iterator(leftMin);
	}

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

2.operator++

在这里插入图片描述

1.cur的右不为空访问右树的最左结点
2.cur的右为空,找到cur是parent左子树的位置,此时parent的位置就是++后的位置

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 = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

3.operator–

–就与++反着来
1.左不为空,找到左树的最右结点
2.左为空,找到cur是parent右的那个结点,此时parent的位置就是–之后的位置

Self& operator--()
	{
		if (_node->_left)
		{
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}

			_node = subRight;
		}
		else
		{
			// 孩子是父亲的右的那个节点
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

六、迭代器拷贝构造特殊处理

template<class T, class Ptr, class Ref>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T, Ptr, Ref> Self;
	
	typedef __TreeIterator<T, T*, T&> Iterator;

	__TreeIterator(const Iterator& it)
		:_node(it._node)
	{}

	Node* _node;

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

}

1.当我们Ptr与Ref分别实例化为T与T&的时候,__TreeIterator(const Iterator& it)就是一个拷贝构造函数,因为Iterator与Self类型相同
2.当我们Ptr与Ref分别实例化为const T
与const T&的时候,__TreeIterator(const Iterator& it)是一个构造,支持普通迭代器构造const类型的迭代器因为Self为const类型,Iterator为普通类型
这里支持用普通迭代器去构造const类型的迭代器,就可以满足我们set的插入功能的实现

typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
pair<iterator,bool>insert(const K&key){
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
//这里RBTree里面的iterator类型为普通迭代器类型,而我们返回值里面的pair中的iterator为const类型,
//所以要想返回必须先把RBTree中的iterator变为const类型,这个时候可以拷贝构造
//让普通迭代器变为const类型的迭代器
		return pair<iterator, bool>(ret.first, ret.second);
	}

7.源码

这里会涉及到红黑树的一些变色问题,之前的博客有提到过【C++】红黑树插入操作实现以及验证红黑树是否正确
需要的小伙伴可以去看一下

RBTree.h

#pragma once
#include<iostream>
using namespace std;

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),
		_col(RED),
		_data(data)
	{}
};


template<class T, class Ptr, class Ref>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T, Ptr, Ref> Self;
	
	typedef __TreeIterator<T, T*, T&> Iterator;

	__TreeIterator(const Iterator& it)
		:_node(it._node)
	{}

	Node* _node;

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

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

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

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

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

	Self& operator--()
	{
		if (_node->_left)
		{
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}

			_node = subRight;
		}
		else
		{
			// 孩子是父亲的右的那个节点
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	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 = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}
};


template<class K,class T,class KeyOfT>
class RBTree {
	typedef RBTreeNode<T> Node;
public:
	// 同一个类模板,传的不同的参数实例化出的不同类型
	typedef __TreeIterator<T, T*, T&> iterator;
	typedef __TreeIterator<T, const T*, const T&> const_iterator;

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

		return iterator(leftMin);
	}

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

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

		return const_iterator(leftMin);
	}

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

	Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;//KeyOfT为仿函数的类型
		//写好仿函数后先实例化出来
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}


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

		Node* parent = nullptr;
		Node* cur = _root;

		KeyOfT kot;
		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);
		cur->_col = RED;

		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;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // u不存在 或 存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g
						//   p
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//		c
						RotateL(parent);
						RotateR(grandfather);

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

					break;
				}
			}
			else // parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						// g
						//	  p
						//       c
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						// g
						//	  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* cur = parent->_right;
		Node* curleft = cur->_left;

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

		cur->_left = parent;

		Node* ppnode = parent->_parent;

		parent->_parent = cur;


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

			}

			cur->_parent = ppnode;
		}
	}


	void RotateR(Node* parent)
	{
		 

		Node* cur = parent->_left;
		Node* curright = cur->_right;

		parent->_left = curright;
		if (curright)
			curright->_parent = parent;

		Node* ppnode = parent->_parent;
		cur->_right = parent;
		parent->_parent = cur;

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

			cur->_parent = ppnode;
		}
	}
private:
	Node* _root = nullptr;
};

2.MyMap.h

#pragma once
#include"RBTree.h"
namespace bit {
	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, pair<const K, V>, 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();
		}

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

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


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

	};

}

3.MySet.h

#pragma once
#include"RBTree.h"


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


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

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

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

		pair<iterator,bool>insert(const K&key){
			pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
			return pair<iterator, bool>(ret.first, ret.second);
	}


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

	};
}

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

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

相关文章

A Framework to Evaluate Fusion Methods for Multimodal Emotion Recognition

题目A Framework to Evaluate Fusion Methods for Multimodal Emotion Recognition译题一种评估多模态情感识别融合方法的框架时间2022年仅用于记录学习&#xff0c;不作为商用 一种评估多模态情感识别融合方法的框架 摘要&#xff1a;情绪识别的多模态方法考虑了预测情绪的几…

2023/9/17周报

摘要 本周阅读了两篇论文&#xff0c;其一为一种基于空气质量时频域特征提取的hybrid预测方法&#xff0c;另一篇为基于烛台与视觉几何群模型的 PM2.5 变化趋势特征提取与分类预测方法。在第一篇文章中&#xff0c;通过小波变化&#xff0c;对数据进行分频&#xff0c;并设计了…

详解3dMax中渲染线框的两种简单方法

在3dMax中渲染线框是你在某个时候想要完成的事情&#xff0c;例如为了演示分解步骤&#xff0c;或是仅仅为了在模型上创建线框覆盖的独特效果。为三维模型渲染线框最常见的原因是能够在模型上显示干净的拓扑。这篇文章将带你了解在3dMax中渲染三维模型线框的两种最常见、最简单…

太炫酷,3分钟学会,视频倒放技能

一&#xff0c;视频倒放 视频倒放是一种有趣的视频编辑技术&#xff0c;可以为您的视频带来一些特殊的效果。通过倒放视频&#xff0c;您可以实现以下效果&#xff08;如果有其它需要的软件和技术&#xff0c;可以私信小编&#xff1b;更多精彩可关注微信公众号&#xff1a;黑…

推荐一个高质量专栏:「前端面试必备」

文章目录 专栏作者介绍专栏介绍目录&#xff08;前25篇&#xff09;目录&#xff08;后25篇&#xff09;专栏文章部分摘抄JavaScriptVue网络请求和HTTPNode.jswebpackBabelVite微信小程序Vuexuni-appGitECharts前端工程化 写在结尾 专栏作者介绍 &#x1f90d; 前端开发工程师&…

DT Paint Effects工具(三)

管 分支 使用细枝 叶 力 使用湍流 流动画 渲染全局参数 建造盆栽植物

Ubuntu 22.04安装过程

iso下载地址 Ubuntu Releases 1.进入引导菜单 选择Try or Install Ubuntu Server安装 2.选择安装语言 默认选择English 3.选择键盘布局 默认即可 4.选择安装服务器版本 最小化安装 5.配置网络 选择ipv4 选择自定义 DHCP也可 6.配置代理 有需要可以配置 这里跳过 7.软件源 …

「UG/NX」Block UI 指定轴SpecifyAxis

✨博客主页何曾参静谧的博客📌文章专栏「UG/NX」BlockUI集合📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C+&#

第9关:生成器与 yield

任务描述 Python 中存在着一个特殊的函数:生成器。生成器是一个“函数对象”&#xff0c;它与函数的定义在形式上完全相同&#xff0c;具有“函数名”与“参数列表”&#xff0c;不同之处在于它可以以yield方式“暂时返回”。 本关的任务是让学习者掌握 Python 中生成器的使用…

onnxruntime(c++)

https://huaweicloud.csdn.net/63807fb7dacf622b8df89158.htmlhttps://huaweicloud.csdn.net/63807fb7dacf622b8df89158.html基于onnxruntime的C版本CPU/GPU源码编译_三横先生的博客-CSDN博客_onnxruntime gpu c基于onnxruntime的C版本CPU/GPU源码编译提示&#xff1a;基于onnx…

位图原理及实现

位图原理及实现 位图概念位图实现构造函数set成员函数reset成员函数test成员函数 位图的应用 位图概念 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。 对于上面这道题大多数人都会想到用遍历和二分查找…

什么是Webpack的Tree Shaking?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Webpack的Tree Shaking⭐ 作用和原理⭐ 使用 Tree Shaking⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感…

2023年免疫细胞治疗行业研究报告

第一章 行业概况 1.1 定义及分类 免疫细胞治疗是细胞治疗的一种重要分支&#xff0c;它涉及对免疫细胞进行体外操作&#xff0c;然后将其回输到人体内&#xff0c;以实现对肿瘤细胞的杀伤或清除病毒等功能。这种治疗方法的核心是利用免疫细胞的自然功能&#xff0c;通过体外技…

【构造函数和原型】

构造函数和原型 1 本节目标2 构造函数和原型2.1 概述2.2 构造函数2.3 构造函数的问题2.4 构造函数原型prototype2.5 对象原型__proto__2.6 constructor构造函数2.7 构造函数、实例、原型对象三者之间的关系2.8 原型链2.9 JavaScript的成员查找机制(规则)2.10 原型对象this指向2…

Qt UDP传送图片

Qt UDP传送图片&#xff0c;server发送&#xff0c;client接收。 server #include "mainwindow.h" #include "ui_mainwindow.h" #include <QTime> #include <QPainter>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new…

求解灰度直方图,如何绘制灰度直方图(数字图像处理大题复习 P1)

文章目录 1. 画 X 轴2. 画直方图3. Complete 视频原链接 数字图像处理期末考试大题 B站链接 1. 画 X 轴 2. 画直方图 有几个 0 就在图上画多高&#xff0c;同理有几个 1 &#xff0c;X1 的地方就画多高 3. Complete 这里的情况比较平均&#xff0c;一般来说不会这么平均&a…

element -ui table表格内容无限滚动 使用插件vue-seamless-scroll

使用插件 一、安装组件依赖 npm install vue-seamless-scroll 二、引入组件 import vueSeamlessScroll from "vue-seamless-scroll"; components: { vueSeamlessScroll }, <div class"table-list "><vue-seamless-scroll :class-option"…

JavaWeb概念视频笔记

学习地址&#xff1a;102.尚硅谷_Tomcat-Tomcat服务器和Servlet版本的对应关系_哔哩哔哩_bilibili 目录 1.JavaWeb的概念 2.Web资源的分类 3.常用的Web服务器 4.Tomcat服务器和Servlet版本的对应关系 5.Tomcat的使用 a.安装 b.目录介绍 c.如何启动 Tomcat 服务器 另一…

【C语言】指针和数组笔试题解析(1)

指针是C语言的灵魂&#xff0c;他的玩法多种多样&#xff0c;这篇文章带来指针的笔试题详解&#xff0c;可以帮助我们更好的理解与巩固指针的知识 目录 预备知识&#xff1a;题目&#xff1a;一维数组&#xff1a;二维数组&#xff1a; 题目比较多&#xff0c;但切记戒骄戒躁&a…

Selenium常用操作之单选复选框、下拉列表、键盘、截屏、断言、(显式隐式)等待

目录 1. 窗口最大化 2.单选框操作 3. 复选框操作 4. 下拉列表 5. selenium 三种等待 6. 键盘操作 7.截屏 8.断言 9. Selenium操作JS弹窗控件 10.鼠标悬停与释放 1. 窗口最大化 driver.maximize_window() 2.单选框操作 driver.find_element_by_xpath("//input[…