数据结构: map与set的简单实现

news2024/9/20 19:40:16

目录

map与set的模拟实现

1.基本框架

2.模拟实现map与set所需要做的事

1.使用模板 , 达到泛性编程

2.比较问题

3.迭代器

RBTree中:

operator++

operator--

4.map [ ]  的实现

5.使用普通迭代器构造const迭代器

效果


map与set的模拟实现

1.基本框架

map

set

2.模拟实现map与set所需要做的事

  1. 调用模板, 使得用map与set复用同一棵树(解决map存pair, set存key的问题)
  2. 比较问题
  3. 迭代器中, ++,--的实现(遍历二叉树)
  4. map[ ] 的实现
  5. 维护底层搜索二叉树的性质, 要使用const迭代器, set可以都用const迭代器, 但是map需要达到key不能修改, 但允许value可以修改, 所以红黑树也需要实现普通迭代器,--使得key不能修改-->map再传递pair的时候,给K加上const就行                                                -->set的iterator 是红黑树中的const_iterator && const_iterator也是const_iterator            -->map的使用了iterator 与 const_iterator                                                                         set的iterator调用的时候, 会发生隐式类型转换,  普通迭代器-->const迭代器                     如果没提供普通迭代器到const迭代器的构造会报错  --> 需要提供

1.使用模板 , 达到泛性编程

   map: kv类型   set : k类型   

map:

set:


2.比较问题

由于使用了泛性编程, RBTreeNode里面的_data类型不确定, 可能是K, 也可能是pair

--如果是K(set)的比较,不会有问题

--如果是pair(map)的比较, 比较会出现问题

pair的比较: 如果first小就小, 如果first不小, 比较second, second小就小

但我们只期望按K去比较


解决: 使用仿函数

红黑树并不知道传过来的T是K类型还是pair类型, 但是上一层知道, 于是我们给它加一个仿函数,

这个仿函数在map/set里面实现 用来获取key,然后传递给红黑树, 让其根据key来比较

像这样:

map

set


用法:

data  为T类型, 可能是K类型, 也可能是pair类型,我们将其传递给仿函数,来获取key

1.插入

2.查找

   Node* cur ->_data  为T类型, 可能是K类型, 也可能是pair类型,我们将其传递给仿函数,来获取key

  

3.迭代器

map与set的迭代器是使用红黑树内部的迭代器

1.注意: 当我们去取一个类模板的内嵌类型的时候, 前面要加一个typename

2. 原因:  因为编译器无法区分, 你取得是类型还是静态变量 

加上typename告诉编译器所取得是类型, 等该类模板实例化后, 再去找这个类型

3.示例:

map

set

框架:

RBTree中:

begin

使用最左边的节点构造迭代器

end

1.使用空节点构造迭代器(一般是最右边的节点的下一个--->是nullptr)

2.实现的时候增加哨兵位

--此时如果it走到最后一个节点,再++, 就直接走到end

--特殊处理:

优点:end--的时候, 直接到最右边的节点

operator++

左子树,  根 ,  右子树  (中序,找完右子树后,  其树就被访问完了)
思路:
1.有右子树: 就往右子树走-->找右子树的最左节点

2.没有右子树: 看是不是parent的左子树
--是的话就把parent给it
--不是的话向上调整    (调整最后没有父亲节点了,跳空)

沿着根路径, 找孩子是父亲左孩子的那个祖先


对以下情况演示:

1.该节点有右子树

2.该节点没有右子树

3.走到最后一个节点

代码:

	Self& operator++() 
	{
		//1.右子树不为空,找右子树的最左节点
		if (_node->_right) 
		{
			Node* subRL = _node->_right;
			while (subRL->_left) 
			{
				subRL = subRL->_left;
			}
			_node = subRL;
		}
		//2.右子树为空, 找节点是父亲左孩子的祖先
		else 
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}

		return *this;
	}

operator--

和上面基本一样,只是换了个方向: 右子树,  根 ,  左子树(该顺序访问, 走完左子树,该树就走完)

思路:

1.有左子树, 就往左子树走--->找左子树里最右边节点

2.没有左子树, 就找节点是父亲的右孩子的祖先

--如果当前节点就是父亲的右孩子,  直接把parent给it

--如果当前节点不是父亲的右孩子, 沿根路径向上调整, 找到为止(或找到p为nullptr)

   找到了就把parent给it

代码:

	Self& operator--()
	{
		//1.如果有左子树,就往左子树走,找其最右边的节点
		if (_node->_left) 
		{
			Node* subLR = _node->_left;
			while (subLR->_right) 
			{
				subLR = subLR->_right;
			}
			//然后把这个节点给it
			_node = subLR;
		}
		//2.如果没有左子树, 就找节点是父亲右孩子的祖先
		else 
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left == cur) 
			{
				cur = parent;
				parent = parent->_parent;
			}
			//然后把这个节点给it
			_node = parent;
		}

		return *this;
	}

4.map [ ]  的实现

1.修改insert的返回值为pair<iterator,bool>

--修改RBTree里Insert的返回值:

--空树插入成功

--非空树插入失败

--非空树插入成功

这里使用newnode记录以下cur节点,  下面需要对红黑树调整, cur 可能会发生改变

2.[ ]的实现

--调用RBTree里的Insert函数

--返回其second

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

		//[]的实现
		V& operator[](const K& key) 
		{
			pair<iterator,bool> ret =  _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}

5.使用普通迭代器构造const迭代器

1.问题:如果使用普通迭代器, 那么key的值会被修改, 不满足二叉搜索树性质

示例:

2.我们使用const迭代器

出现报错

--原因:发生隐式类型转换, 但我们没有提供使用普通迭代器构造const迭代器的构造函数,

转换不了, 会报错

--解决: 提供一个支持普通迭代器到const迭代器的转换

--为什么RBTree不都返回const迭代器??

---因为map与set复用的是同一个红黑树来set全用const可以, 但map允许修改V

代码:

--当Ref是T&, Ptr是T*的时候, 调用这个构造函数就是拷贝构造

--当Ref是constT& ,Ptr是constT*的时候, 调用这个构造函数就是支持普通迭代器构造const迭代器的转换

效果

代码:Map and Set/Map and Set · 朱垚/数据结构练习 - 码云 - 开源中国 (gitee.com)

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

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

相关文章

nodejs+vue旅行社网站系统-计算机毕业设计

激发了管理人员的创造性与主动性&#xff0c;对旅行社网站系统而言非常有利。 模块包括主界面&#xff0c;首页、个人中心、用户管理、景点分类管理、旅游景点管理、景点购票管理、酒店信息管理、酒店预定管理、旅游路线管理、系统管理等进行相应的操作。 目 录 摘 要 I ABST…

力扣刷题 day52:10-22

1.数组拆分 给定长度为 2n 的整数数组 nums &#xff0c;你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) &#xff0c;使得从 1 到 n 的 min(ai, bi) 总和最大。 返回该 最大总和 。 方法一&#xff1a;排序 #方法一&#xff1a;排序 def arrayPai…

【计算机网络】UDP的报文结构和注意事项

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是一种无连接的协议&#xff0c;它在传输层中提供了简单、不可靠的数据传输服务。与TCP&#xff08;Transmission Control Protocol,传输控制协议&#xff09;不同&#xff0c;UDP不需要建立连接&…

驱动开发3 ioctl函数的使用+3个实例(不传递第三个参数、第三个参数为整型、第三个参数为地址)

开发板&#xff1a;stm32mp157aaa&#xff08;Cortex-A7*2 Cortex-M4*1&#xff09;开发环境&#xff1a;vscode、串口工具 1 引入ioctl函数的意义 linux操作系统中有意将数据的读写和读写功能的选择分别交给不同的函数去完成。就让read/write函数只进行数据的读写即可&#x…

手把手创建属于自己的ASP.NET Croe Web API项目

第一步&#xff1a;创建项目的时候选择ASP.NET Croe Web API 点击下一步&#xff0c;然后配置&#xff1a; 下一步&#xff1a;

Openssl数据安全传输平台007:共享内存及代码的实现 ——待完善项目具体代码和逻辑

文章目录 0. 代码仓库1. 使用流程案例代码&#xff1a; 2. API解析2.1 创建或打开一块共享内存区2.2 将当前进程和共享内存关联到一起2.3 将共享内存和当前进程分离2.4 共享内存操作 -&#xff08; 删除共享内存 &#xff09; 3. 思考问题3. ftok函数4. 共享内存API封装-以本项…

rust学习——栈、堆、所有权

文章目录 栈、堆、所有权栈(Stack)与堆(Heap)栈堆性能区别所有权与堆栈 所有权原则变量作用域所有权与函数返回值与作用域 栈、堆、所有权 栈(Stack)与堆(Heap) 栈和堆是编程语言最核心的数据结构&#xff0c;但是在很多语言中&#xff0c;你并不需要深入了解栈与堆。 但对于…

互联网Java工程师面试题·Java 面试篇·第五弹

目录 79、适配器模式和装饰器模式有什么区别&#xff1f; 80、适配器模式和代理模式之前有什么不同&#xff1f; 81、什么是模板方法模式&#xff1f; 82、什么时候使用访问者模式&#xff1f; 83、什么时候使用组合模式&#xff1f; 84、继承和组合之间有什么不同&#…

【BIGRU预测】基于双向门控循环单元的多变量时间序列预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

改进YOLO系列 | YOLOv5/v7 引入 Dynamic Snake Convolution | 动态蛇形卷积

准确分割拓扑管状结构,如血管和道路,在各个领域中至关重要,可以确保下游任务的准确性和效率。然而,许多因素使任务复杂化,包括细小的局部结构和可变的全局形态。在这项工作中,我们注意到管状结构的特殊性,并利用这一知识来引导我们的DSCNet,以在三个阶段同时增强感知:…

1 Go的前世今生

概述 Go语言正式发布于2009年11月&#xff0c;由Google主导开发。它是一种针对多处理器系统应用程序的编程语言&#xff0c;被设计成一种系统级语言&#xff0c;具有非常强大和有用的特性。Go语言的程序速度可以与C、C相媲美&#xff0c;同时更加安全&#xff0c;支持并行进程。…

微信小程序设计之主体文件app-json-tabBar

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

LeetCode讲解篇之40. 组合总和 II

文章目录 题目描述题解思路题解代码 题目描述 题解思路 按升序排序candidates&#xff0c;然后遍历candidates&#xff0c;目标数减去当前candidates的数&#xff0c;若该结果小于0&#xff0c;因为candidates的元素大于0&#xff0c;所以后续不会再出现让计算结果等于0的情况…

021-Qt 配置GitHub Copilot

Qt 配置GitHub Copilot 文章目录 Qt 配置GitHub Copilot项目介绍 GitHub Copilot配置 GitHub CopilotQt 前置条件升级QtGitHub Copilot 前置条件激活的了GitHub Copilot账号安装 Neovim 启用插件&#xff0c;重启Qt配置 GitHub Copilo安装Nodejs下载[copilot.vim](https://gith…

Openssl数据安全传输平台004:Socket C-API封装为C++类 / 服务端及客户端代码框架和实现

文章目录 0. 代码仓库1. 客户端C API2. 客户端C API的封装分析2.1 sckClient_init()和sckClient_destroy()2.2 sckClient_connect2.3 sckClient_closeconn()2.4 sckClient_send()2.5 sckClient_rev()2.6 sck_FreeMem 3. 客户端C API4. 服务端C API5. 服务端C6. 客户端和服务端代…

MySQL-DML【数据操作语言】(图码结合)

目录 &#x1f6a9;DML的定义 &#x1f449;DML-添加数据 &#x1f393;给指定的字段添加数据 &#x1f576;️查询表数据的方式 ❗疑惑点一【Affecter rows:行数】 ❗疑惑点二【字符集问题】 &#x1f393;给全部字段添加数据 &#x1f393;批量添加数据 &#x1f…

System Design现代系统设计概论

1. 什么是系统设计&#xff1f; 系统设计是定义组件及其集成、API 和数据模型以构建满足一组指定功能和非功能需求的大型系统的过程。 系统设计使用计算机网络、并行计算和分布式系统的概念来设计可扩展且高性能的系统。分布式系统本质上具有良好的可扩展性。然而&#xff0c…

antd组件onChange回调,需要立即执行改变value与防抖节省接口开销。

文章目录 普通使用使用防抖节省开销页面功能复杂需要value受控回调需要部分代码立即执行&#xff0c;部分代码防抖延时执行useRefuseCallback 小结 普通使用 当我们使用Antd的input或者select进行搜索时&#xff0c;onChange回调会即时执行。 import { Input } from "an…

R155法规有没有要求上Secure Boot功能?

标签&#xff1a; R155法规有没有要求上Secure Boot功能&#xff1f;&#xff1b; R155法规有没有要求上安全启动功能&#xff1f;&#xff1b;R155法规与Secure Boot关系&#xff1f;R155法规可以不上Secure Boot吗&#xff1f; R155法规有没有要求一定要上Secure Boot&#…

Node学习笔记之Express框架

一、express 介绍 express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架&#xff0c;官方网址&#xff1a;https://www.expressjs. com.cn/ 简单来说&#xff0c;express 是一个封装好的工具包&#xff0c;封装了很多功能&#xff0c;便于我们开发 WEB 应用&…