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

文章目录

  • 一、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// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p//		cRotateL(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//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(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.mzph.cn/news/80779.shtml

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

相关文章

傅里叶变换应用 (02/2):频域和相位

一、说明 到目前为止&#xff0c;在我们的讨论中&#xff0c;我已经交替使用了“傅里叶变换”和“快速傅里叶变换&#xff08;FFT&#xff09;”。在这一点上&#xff0c;值得注意的是区别&#xff01;FFT 是“离散”傅里叶变换 &#xff08;DFT&#xff09; 的有效算法实现。“…

JavaScript-Ajax-axios-Xhr

JS的异步请求 主要有xhr xmlHttpRequest 以及axios 下面给出代码以及详细用法&#xff0c;都写在了注释里 直接拿去用即可 测试中默认的密码为123456 账号admin 其他一律返回登录失败 代码实例 <!DOCTYPE html> <html lang"en"> <head><…

科技抗老新突破,香港美容仪品牌内地重磅上市

近年来&#xff0c;新消费时代“颜值经济”的火热促使美容行业市场规模增长迅速&#xff0c;越来越多的人愿意为“美”买单&#xff0c;对美的需求也随之增长&#xff0c;美容行业已经成为成长最快的新锐产业。随着经济和科技的发展&#xff0c;“快捷”也成为了当今社会的时代…

想要精通算法和SQL的成长之路 - 最长回文子串

想要精通算法和SQL的成长之路 - 最长回文子串 前言一. 最长回文子串1.1 中心扩散法的运用 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 最长回文子串 原题链接 1.1 中心扩散法的运用 这类具有回文性质的题目&#xff0c;我们如果用常规的从左往右或者从右往左的遍历方…

中尺度混凝土二维有限元求解——运行弯曲、运行光盘、运行比较、运行半圆形(Matlab代码实现)

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

swift 约束布局

添加约束布局 背景图瀑全屏 如何三等分 外面view容器没有约束

【Spring Boot】Spring—加载监听器

这里写目录标题 前言加载监听器执行run方法加载配置文件封装Node调用构造器思考 前言 前几天的时候&#xff0c;项目里有一个需求&#xff0c;需要一个开关控制代码中是否执行一段逻辑&#xff0c;于是理所当然的在yml文件中配置了一个属性作为开关&#xff0c;再配合nacos就可…

笔记1.4 计算机网络性能

1. 速率 速率即数据率&#xff08;data rate&#xff09;或称数据传输速率或比特率 单位时间&#xff08;秒&#xff09;传输信息&#xff08;比特&#xff09;量 计算机网络中最重要的一个性能指标 单位&#xff1a;bps、kbps、Mbps k 10^3、M 10^6、G 10^9 速率往往…

网络安全深入学习第一课——热门框架漏洞(RCE-命令执行)

文章目录 一、RCE二、命令执行/注入-概述三、命令执行-常见函数四、PHP命令执行-常见函数1、exec&#xff1a;2、system3、passthru4、shell_exec5、反引号 backquote 五、PHP命令执行-常见函数总结六、命令执行漏洞成因七、命令执行漏洞利用条件八、命令执行漏洞分类1、代码层…

excel中的引用与查找函数篇2

如下所有案例中表头均不参与范围查找内&#xff1a; 1、LOOKUP(lookup_value,lookup_vector,[result_vector])&#xff1a;在一行或者一列中查找某个值并从另一行或者列中找到同位置的值 记住&#xff1a;中括号内的参数可以不赋值&#xff0c;若在中间用逗号隔开这个参数&…

思维模型 协议

1 模型故事 1.1 社会性质的协议 1 世界观的建立 1 2 3 4 5 6 7 8 9 0 这些阿拉伯数字 如此常见&#xff0c;那么我们是否想过 为什么 这些阿拉伯数字我们如此熟悉&#xff1f;为什么我们要学习这些玩意儿&#xff1f;这些东西为什么大家都要学习&#xff0c;都要使用&#x…

C++数据结构X篇_14_二叉树的递归遍历(先序遍历、中序遍历、后续遍历方法介绍;举例;代码实现)

我们知道数据的存储结构分为线性与非线性。线性就是1对1的结构&#xff0c;像栈与队列都属于线性结构。那什么是非线性的结构呢&#xff1f; 非线性即1对n的结构这更符合常规情况&#xff0c;线性结构本质上属于非线性结构中的一种特殊形式&#xff0c;像树就属于非线性结构。但…

neo4j下载安装配置步骤

目录 一、介绍 简介 Neo4j和JDK版本对应 二、下载 官网下载 直接获取 三、解压缩安装 四、配置环境变量 五、启动测试 一、介绍 简介 Neo4j是一款高性能的图数据库&#xff0c;专门用于存储和处理图形数据。它采用节点、关系和属性的图形结构&#xff0c;非常适用于…

6. 装饰器

UML 聚合(Aggregation)关系&#xff1a;大雁和雁群&#xff0c;上图中空心菱形箭头表示聚合关系组合(Composition)关系&#xff1a;大雁和翅膀 &#xff0c;实心菱形箭头表示组合(Composition)关系 测试代码 #include <iostream> #include <stdio.h> #include &l…

Spring复杂对象的3中创建方法

复杂对象是相对于简单对象可以直接 new 出的对象。这种对象在 Spring 中不可以通过简单对象的创建方式来创建。下面我们将通过实现 FactoryBean 接口、实例工厂、静态工厂三种方法来创建。 FactoryBean 接口 Spring 提供 FactoryBean 接口并且提供了 getObject 方法是为了支持…

“熊猫杯” | 赛宁网安获网络安全优秀创新成果大赛优胜奖

9月11日&#xff0c;四川省2023年国家网络安全宣传周正式启动。由四川省委网信办指导&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;主办&#xff0c;成都信息工程大学、四川省网络空间安全协会承办的“2023年网络安全优秀创新成果大赛—成都分站赛(暨四川省‘…

Spring Boot - 用JUnit 5构建完美的Spring Boot测试套件

文章目录 PreJUnit 4 vs JUnit 5Junit5 常用注解栗子 Pre SpringBoot - 单元测试利器Mockito入门 SpringBoot - 应用程序测试方案 SpringBoot - SpringBootTest加速单元测试的小窍门 Spring Boot - Junit4 / Junit5 / Spring Boot / IDEA 关系梳理 package org.junit.jupit…

Excel VBA 变量,数据类型常量

几乎所有计算机程序中都使用变量&#xff0c;VBA 也不例外。 在过程开始时声明变量是一个好习惯。 这不是必需的&#xff0c;但有助于识别内容的性质&#xff08;文本&#xff0c;​​数据&#xff0c;数字等&#xff09; 在本教程中&#xff0c;您将学习- 一、VBA变量 变量是…

Unity中程序集dll

一&#xff1a;前言 一个程序集由一个或多个文件组成&#xff0c;通常为扩展名.exe和.dll的文件称为程序集&#xff0c;.exe是静态的程序集&#xff0c;可以在.net下直接运行加载&#xff0c;因为exe中有一个main函数(入口函数&#xff09;&#xff0c;.dll是动态链接库&#…

腾讯mini项目-【指标监控服务重构】2023-08-04

今日已办 关于 span-references 的调研 https://github.com/DataDog/dd-trace-js/issues/1761 https://github.com/open-telemetry/opentelemetry-specification/blob/874a451e7f6ac7fc54423ee3f03e5394197be35b/specification/compatibility/opentracing.md#span-references h…