STL set 和 map

文章目录

  • 一、标准库中 set 和 multiset 的使用
  • 二、标准库中 map 和 multimap 的使用
  • 三、set 和 map 底层红黑树的模拟实现
  • 四、set 类 和 map 类的模拟实现

一、标准库中 set 和 multiset 的使用

在这里插入图片描述

在这里插入图片描述

set 是一颗 K 模型的红黑树,可以存储任意类型,multiset 和 set 的区别是 multiset 允许插入重复值,set 不允许

模板参数 T 表示存储元素的类型,Compare 表示存储元素的比较方法,Alloc 是空间配置器,一般不用传

set 和 multiset 常用接口使用:

#include <iostream>
#include <set>using namespace std;void setUsing()
{// set 不允许插入重复值// insert 返回值为 pair<iterator, bool>// 如果插入的值不存在,则返回值 pair 中的 first 指向新插入的元素,second 设置为 true// 如果插入的值已存在,则返回值 pair 中的 first 指向已有的元素,second 设置为 falseset<int> s;cout << s.insert(3).second << " ";cout << s.insert(1).second << " ";cout << s.insert(1).second << " ";cout << s.insert(4).second << " ";cout << s.insert(2).second << " ";cout << s.insert(1).second << " ";cout << endl; // 输出 1 1 0 1 1 0// set 不允许修改元素内容,因此 iterator 和 const_iterator 是相同的set<int>::iterator sit = s.begin();while (sit != s.end()){// *sit *= 10;cout << *sit << " ";++sit;}cout << endl; // 输出 1 2 3 4// find 成功返回指向该元素的迭代器,失败返回 end()sit = s.find(1);if (sit != s.end()) cout << *sit << endl; // 输出 1// 返回 set 中元素的数目cout << s.count(1) << endl; // 输出 1
}void multisetUsing()
{// multiset 允许插入重复值std::multiset<int> s;s.insert(3);s.insert(1);s.insert(1);s.insert(4);s.insert(2);s.insert(1);for (auto e : s) cout << e << " ";cout << endl; // 输出 1 1 1 2 3 4// find 成功返回指向该元素的迭代器,失败返回 end()std::set<int>::iterator sit = s.find(1);if (sit != s.end()) cout << *sit << endl; // 输出 1// 返回 set 中元素的数目cout << s.count(1) << endl; // 输出 3
}int main()
{setUsing();cout << endl;multisetUsing();cout << endl;return 0;
}

二、标准库中 map 和 multimap 的使用

在这里插入图片描述

在这里插入图片描述

map 是一颗 KV 模型的红黑树,可以存储任意类型,multimap 和 map 的区别是 multimap 允许插入 key 值重复的 <key, value> 键值对,map 不允许

模板参数 Key 表示 <key, value> 中 key 的类型,T 表示 <key, value> 中 value 的类型,Compare 表示存储元素的比较方法,Alloc 是空间配置器,一般不用传

map 和 multimap 常用接口使用:

#include <iostream>
#include <map>
#include <string>using namespace std;void mapUsing1()
{// map 不允许插入 key 值重复的 <key, value> 键值对// insert 返回值为 pair<iterator, bool>// 如果插入的 key 值不存在,则返回值 pair 中的 first 指向新插入的元素,second 设置为 true// 如果插入的 key 值已存在,则返回值 pair 中的 first 指向已有的元素,second 设置为 falsemap<string, string> dict;cout << dict.insert(make_pair("sort", "排序")).second << " ";cout << dict.insert(make_pair("string", "字符串")).second << " ";cout << dict.insert(make_pair("count", "计数")).second << " ";cout << dict.insert(make_pair("string", "\"字符串\"")).second << " ";cout << endl; // 输出 1 1 1 0// map 不允许修改元素内容,因此 iterator 和 const_iterator 是相同的map<string, string>::iterator mit = dict.begin();while (mit != dict.end()){cout << mit->first << ":" << mit->second << " ";++mit;}cout << endl; // 输出 count:计数 sort:排序 string:字符串 // find 通过 key 查找,成功返回指向该元素的迭代器,失败返回 end() mit = dict.find("string");if (mit != dict.end()) cout << mit->second << endl; // 输出 字符串// 返回 map 中 key 的数目cout << dict.count("string") << endl; // 输出 1// operator[key] 等价于 ( *(( this->insert(make_pair(key, mapped_type())) ).first) ).second// 如果 key 存在,则返回对应的 <key, value> 中 value 的引用// 如果 key 不存在,则先插入 <key, mapped_type()> ,再返回 <key, mapped_type()> 中 value 的引用(这里的 mapped_type 是指 value 的类型)dict["string"] = "\"字符串\"";dict["right"] = "右边";for (auto& e : dict) cout << e.first << ":" << e.second << " ";cout << endl; // 输出 count:计数 right:右边 sort:排序 string:"字符串" 
}// 统计每种水果出现的次数
void mapUsing2()
{string fruit[] = {"西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨"};map<string, int> fruitCount;for (auto &e : fruit) fruitCount[e]++;for (auto &e : fruitCount) cout << e.first << ":" << e.second << " ";cout << endl; // 输出 梨:1 苹果:5 西瓜:4 香蕉:2
}void multimapUsing()
{// multimap 允许插入 key 值重复的 <key, value> 键值对std::multimap<std::string, std::string> m;m.insert(std::make_pair("string", "字符串"));m.insert(std::make_pair("string", "\"字符串\""));for (auto &e : m) cout << e.first << ":" << e.second << " ";cout << endl; // 输出 string:字符串 string:"字符串" // 返回 map 中 key 的数目cout << m.count("string") << endl; // 输出 2
}int main()
{mapUsing1();cout << endl;mapUsing2();cout << endl;multimapUsing();cout << endl;return 0;
}

三、set 和 map 底层红黑树的模拟实现

#pragma once#include <utility>namespace starrycat
{// 结点的颜色enum Color { RED, BLACK };// 红黑树的结点template<class VALUE>struct RBTreeNode{VALUE _data;RBTreeNode<VALUE>* _left;RBTreeNode<VALUE>* _right;RBTreeNode<VALUE>* _parent;Color _color;	// 结点的颜色RBTreeNode<VALUE>(const VALUE& data = VALUE()): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _color(RED)	// 为了方便树的结构调整,新结点默认为红色{}};template<class T, class Ref, class Ptr>struct __rb__iterator{typedef RBTreeNode<T> Node;typedef __rb__iterator<T, Ref, Ptr> Self;typedef __rb__iterator<T, T&, T*> iterator;__rb__iterator<T, Ref, Ptr>(Node* node) : _node(node) {}__rb__iterator<T, Ref, Ptr>(const iterator& it) : _node(it._node) {}bool operator==(const Self& s) const { return _node == s._node; }bool operator!=(const Self& s) const { return _node != s._node; }Ref operator*() const { return _node->_data; }Ptr operator->() const { return &_node->_data; }void increment(){if (_node->_right){// 根据中序遍历(左根右),如果右子树存在,则迭代器的下一个位置为右子树的最左结点Node* cur = _node->_right;while (cur->_left) cur = cur->_left;_node = cur;}else{// 根据中序遍历(左根右),如果右子树不存在,则迭代器的下一个位置为左子树包含当前结点的最近祖先结点Node* cur = _node;Node* parent = _node->_parent;while (parent && parent->_right == cur){cur = parent;parent = parent->_parent;}_node = parent;}}Self& operator++(){increment();return *this;}Self operator++(int){Self tmp = *this;increment();return tmp;}void decrement(){if (_node->_left){// 根据中序遍历(右根左),如果左子树存在,则迭代器的下一个位置为左子树的最右结点Node* cur = _node->_left;while (cur->_right) cur = cur->_right;_node = cur;}else{// 根据中序遍历(右根左),如果左子树不存在,则迭代器的下一个位置为右子树包含当前结点的最近祖先结点Node* cur = _node;Node* parent = _node->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_node = parent;}}Self& operator--(){decrement();return *this;}Self operator--(int){Self tmp = *this;decrement();return tmp;}Node* _node;};template<class K>struct Less{bool operator()(const K& key1, const K& key2){return key1 < key2;}};// 在查找删除数据时,map 需要用 K 作为参数的类型// T 是红黑树结点存储的类型,作为 set 的底层时只存储 key,作为 map 的底层时存储 <const key, value>// 在插入数据时,set 可以直接比较红黑树结点中的 data,而 map 则需要比较红黑树结点中的 data.first// 如果是 set,KEYOFT(data) 返回 data,如果是 map,KEYOFT(data) 返回 data.firsttemplate<class K, class T, class KEYOFT, class COMPARE = Less<K>>class RBTree{typedef RBTreeNode<T> Node;public:typedef __rb__iterator<T, T&, T*> iterator;typedef __rb__iterator<T, const T&, const T*> const_iterator;public:RBTree<K, T, KEYOFT, COMPARE>(): _root(nullptr){}iterator begin(){// 根据中序遍历(左根右),迭代器的起始位置为树的最左结点 Node* cur = _root;while (cur && cur->_left) cur = cur->_left;return cur;}iterator end(){return nullptr;}const_iterator begin() const{// 根据中序遍历(左根右),迭代器的起始位置为树的最左结点 Node* cur = _root;while (cur->_left) cur = cur->_left;return cur;}const_iterator end() const{return nullptr;}// 插入std::pair<iterator, bool> Insert(const T& data){KEYOFT kot;COMPARE cmp;// 按照二叉搜索树的方式插入结点,保证该树插入结点之后还是二叉搜索树if (_root == nullptr){_root = new Node(data);_root->_color = BLACK;return std::make_pair(iterator(_root), true);;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cmp(kot(data), kot(cur->_data))){parent = cur;cur = cur->_left;}else if (cmp(kot(cur->_data), kot(data))){parent = cur;cur = cur->_right;}else return std::make_pair(iterator(cur), false);}Node* newnode = new Node(data);cur = newnode;if (cmp(kot(data), kot(parent->_data))) parent->_left = cur;else parent->_right = cur;cur->_parent = parent;// 更新颜色while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;// u 存在且为红// u 不存在或存在且为黑if (uncle && uncle->_color == RED){grandfather->_color = RED;parent->_color = BLACK;uncle->_color = BLACK;// 继续判断是否违反了红黑树的性质cur = grandfather;parent = grandfather->_parent;}else{// p 为 g 的左,cur 为 p 的左 右单旋// p 为 g 的左,cur 为 p 的右 先左旋再右旋if (parent->_left == cur){RotateR(grandfather);grandfather->_color = RED;parent->_color = BLACK;}else{RotateL(parent);RotateR(grandfather);grandfather->_color = RED;cur->_color = BLACK;}}}else{Node* uncle = grandfather->_left;// u 存在且为红// u 不存在或存在且为黑if (uncle && uncle->_color == RED){grandfather->_color = RED;parent->_color = BLACK;uncle->_color = BLACK;// 继续判断是否违反了红黑树的性质cur = grandfather;parent = grandfather->_parent;}else{// p 为 g 的右,cur 为 p 的右 左单旋// p 为 g 的右,cur 为 p 的左 先右旋再左旋if (parent->_right == cur){RotateL(grandfather);grandfather->_color = RED;parent->_color = BLACK;}else{RotateR(parent);RotateL(grandfather);grandfather->_color = RED;cur->_color = BLACK;}}}}_root->_color = BLACK;return std::make_pair(iterator(newnode), true);}iterator Find(const K& key){KEYOFT kot;COMPARE cmp;Node* cur = _root;while (cur){if (cmp(key, kot(cur->_data))) cur = cur->_left;else if (cmp(kot(cur->_data), key)) cur = cur->_right;else return cur;}return nullptr;}private:void RotateR(Node* parent){Node* pparent = parent->_parent;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;if (pparent == nullptr) _root = subL;else{if (pparent->_left == parent) pparent->_left = subL;else pparent->_right = subL;}subL->_parent = pparent;}void RotateL(Node* parent){Node* pparent = parent->_parent;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (pparent == nullptr) _root = subR;else{if (pparent->_left == parent) pparent->_left = subR;else pparent->_right = subR;}subR->_parent = pparent;}private:Node* _root;};
}

四、set 类 和 map 类的模拟实现

set 类 和 map 类常用接口模拟实现:

// test.cc
#include "set.hpp"
#include "map.hpp"int main()
{starrycat::setTest1();starrycat::mapTest1();starrycat::mapTest2();return 0;
}// set.hpp
#pragma once#include "RBTree.h"
#include <iostream>
#include <utility>namespace starrycat
{template <class K>struct SET_KEYOFT{const K &operator()(const K &key){return key;}};template <class K>class set{public:// set 不允许修改元素内容,因此 iterator 和 const_iterator 是相同的typedef typename RBTree<K, K, SET_KEYOFT<K>>::const_iterator iterator;typedef typename RBTree<K, K, SET_KEYOFT<K>>::const_iterator const_iterator;public:// 红黑树底层实现了 iterator 构造 const_iteratoriterator begin() const { return _rb.begin(); }iterator end() const { return _rb.end(); }std::pair<iterator, bool> insert(const K &key) { return _rb.Insert(key); }private:RBTree<K, K, SET_KEYOFT<K>> _rb;};void setTest1(){set<int> s;int a[] = {16, 3, 7, 11, 9, 26, 18, 14, 15};for (auto e : a) s.insert(e);set<int>::iterator sit = s.begin();while (sit != s.end()){// *sit *= 10;std::cout << *sit << " ";++sit;}std::cout << std::endl; // 输出 3 7 9 11 14 15 16 18 26}
}// map.hpp
#pragma once#include "RBTree.h"
#include <iostream>
#include <string>namespace starrycat
{template<class K, class V>struct MAP_KEYOFT{const K& operator()(const std::pair<const K, V>& data){return data.first;}};template<class K, class V>class map{public:typedef typename RBTree<K, std::pair<const K, V>, MAP_KEYOFT<K, V>>::iterator iterator;typedef iterator const_iterator;public:iterator begin() { return _rb.begin(); }const_iterator begin() const { return _rb.begin(); }iterator end() { return _rb.end(); }const_iterator end() const { return _rb.end(); }std::pair<iterator, bool> insert(const std::pair<K, V>& data) { return _rb.Insert(data); }V& operator[](const K& key) { return ( *(( insert(std::make_pair(key, V())) ).first) ).second; }private:RBTree<K, std::pair<const K, V>, MAP_KEYOFT<K, V>> _rb;};void mapTest1(){map<std::string, std::string> dict;std::cout << dict.insert(std::make_pair("sort", "排序")).second << " ";std::cout << dict.insert(std::make_pair("string", "字符串")).second << " ";std::cout << dict.insert(std::make_pair("count", "计数")).second << " ";std::cout << dict.insert(std::make_pair("string", "\"字符串\"")).second << " ";std::cout << std::endl; // 输出 1 1 1 0map<std::string, std::string>::iterator mit = dict.begin();while (mit != dict.end()){std::cout << mit->first << ":" << mit->second << " ";++mit;}std::cout << std::endl; // 输出 count:计数 sort:排序 string:字符串 dict["string"] = "\"字符串\"";dict["right"] = "右边";for (auto& e : dict) std::cout << e.first << ":" << e.second << " ";std::cout << std::endl; // 输出 count:计数 right:右边 sort:排序 string:"字符串" }// 统计每种水果出现的次数void mapTest2(){std::string fruit[] = {"西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨"};map<std::string, int> fruitCount;for (auto &e : fruit) fruitCount[e]++;for (auto &e : fruitCount) std::cout << e.first << ":" << e.second << " ";std::cout << std::endl; // 输出 梨:1 苹果:5 西瓜:4 香蕉:2}
}

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

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

相关文章

【数据结构】顺序查找,折半查找,分块查找的知识点总结及相应的代码实现

目录 1、顺序查找 定义及步骤 代码实现 2、折半查找 定义及步骤 代码实现 折半查找判定树 3、分块查找 定义及步骤 1、顺序查找 定义及步骤 顺序查找的定义&#xff1a;从数据集合的起始位置开始&#xff0c;逐一比较每个数据元素&#xff0c;直到找到所要查找…

哈希表的模拟实现

unordered_set: 接口函数&#xff1a; 对应的应用&#xff1a; unrodered_map: 对应的函数接口&#xff1a; 对应的应用&#xff1a; 比较set和unordered_set的效率&#xff1a; 可以看到各个方面hashset是优于set的。 哈希表的模拟实现&#xff1a; 哈希表的实现分为两种&…

什么是Peppol ID?如何创建?

Peppol 网络的两大优势是安全和高效&#xff0c;由于Peppol 最常用于电子发票&#xff0c;因此这些优势在电子发票上展露无遗。相比之下&#xff0c;通过电子邮件发送 PDF 格式的发票和其他文件不仅处理成本较高&#xff0c;而且容易出现发票欺诈。 如果您所在的公共部门组织或…

华为云云耀云服务器 L 实例评测:快速建站的新选择,初创企业和开发者的理想之选

华为云云耀云服务器 L 实例评测&#xff1a;快速建站的新选择&#xff0c;初创企业和开发者的理想之选 文章目录 华为云云耀云服务器 L 实例评测&#xff1a;快速建站的新选择&#xff0c;初创企业和开发者的理想之选导语&#xff1a;摘要&#xff1a; 正文产品概述部署简易性步…

使用免费软件将数据从机械硬盘克隆到固态硬盘!

正如大家所知道的那样&#xff0c;固态硬盘无论是在读写速度、功耗、噪声还是在耐用性等许多方面都比机械硬盘要更好&#xff0c;所以现在有越来越多的人想要使用升级硬盘&#xff0c;将自己的旧机械硬盘克隆到固态硬盘&#xff0c;从而优化计算机的性能。 目前市面上…

1、Elasticsearch 8.X 概述与安装

第1章 Elasticsearch 8.X 概述 1.1 Elasticsearch 8.X 距 2019 年 Elasticsearch 上一大版本 7.0 发布至今已经过去了 3 年。2022 年 2 月 11 日&#xff0c;Elasticsearch 发布了全新的 8.0 正式版本&#xff0c;这着实给了我们不 小的惊喜&#xff01;新版本中通过改进 Elas…

局域网点歌系统

网盘下载 1、先打开服务端&#xff0c;设置好IP地址 2、客户端打开连接服务器 3、客户端点歌&#xff0c;服务器即可播放

【RV1103】RTL8723bs (SD卡形状模块)驱动开发

文章目录 前言硬件分析Luckfox Pico的SD卡接口硬件原理图LicheePi zero WiFiBT模块总结 正文Kernel WiFi驱动支持Kernel 设备树支持修改一&#xff1a;修改二&#xff1a; SDK全局配置支持 wifi全局编译脚本支持编译逻辑拷贝rtl8723bs的固件到文件系统的固定目录里面去 上电后手…

jvs-rules(规则引擎)和jvs智能bi(自助式数据分析)9.22更新内容

规则引擎更新功能 新增: 1.新增节点匹配筛选 用于做多个条件的数据筛选&#xff0c;以便将符合条件的数据传递给下一个节点进行处理&#xff0c;通常用于实现复杂的查询逻辑。 2.复合变量节点新增判断条件选项说明 用户可以根据自己的需求&#xff0c;为复合变量节点添加不…

深入学习计算机组成原理文章体系

大家好&#xff0c;欢迎阅读《计算机组成原理》的系列文章&#xff0c;本系列文章主要教内容是从零学习计算机组成原理&#xff0c;内容通俗易懂&#xff0c;大家好好学习吧&#xff01;&#xff01;&#xff01; 更多的优质内容&#xff0c;请点击以下链接查看哦~~ 序号链接…

Java深入理解线程的三大特性

目录 1 CPU缓存导致可见性问题2 线程切换导致原子性问题3 性能优化导致有序性问题4 JMM(Java Memory Model)5 volatile6 synchronized 1 CPU缓存导致可见性问题 线程的三大特性&#xff1a; 可见性&#xff1a;Visibility有序性&#xff1a;Ordering原子性&#xff1a;Atomic…

ShapeableImageView 不只是圆形ImageView

偶然间看到了这位老哥的 https://juejin.cn/post/6869376452040196109#comment 文章&#xff0c;发现了ShapeableImageView–一个多形状的ImageView &#xff0c;虽然似乎发布了很久了&#xff0c;现在学习不晚。 效果图 布局文件 <com.google.android.material.imageview.S…

图形处理软件Photoshop Elements 2020 mac中文版 ps简化版

Photoshop Elements 2020 mac是一款非常实用的图形处理工具。ps elements 2020 mac中文版可以帮助您自动生成照片和视频作品的功能&#xff0c;采用Adobe Sensei AI技术可进行图像组织、编辑和创建等。Photoshop Elements 2020 for Mac激活版可以帮助您轻松整理照片和视频&…

【LeetCode热题100】--189.轮转数组

189.轮转数组 数组翻转&#xff1a; 当我们将数组的元素向右移动k次后&#xff0c;尾部k mod n个元素会移动至数组 头部&#xff0c;其余元素向后移动k mod n个位置 该方法为数组的翻转&#xff1a;我们可以先将所有元素翻转&#xff0c;这样尾部k mod n个元素就被移至数组头…

ROS2 从头开始:第 08/8回 - 使用 ROS2 生命周期节点简化机器人软件组件管理

一、说明 欢迎来到我在 ROS2 上的系列的第八部分。对于那些可能不熟悉该系列的人,我已经涵盖了一系列主题,包括 ROS2 简介、如何创建发布者和订阅者、自定义消息和服务创建、

医学影像SAM

医学影像SAM 1. 医学影像SAM1.1. MedSAM1.2. SAM-Adapter1.3. Medical-SAM-Adapter1.4. sam-med2d1.5. MS-SAM 下面整理了一些比较好的博客。 1. 医学影像SAM 由于sam在医学影像上表现不是特别好&#xff0c;在该类型数据集上就需要再训练。 1.1. MedSAM MedSAM&#xff1a…

WebGL绘制圆形的点

目录 前言 如何实现圆形的点&#xff1f; 片元着色器内置变量&#xff08;gl_FragCoord、gl_PointCoord&#xff09; gl_PointCoord的含义 示例程序&#xff08;RoundedPoint.js&#xff09; 代码详解 前言 本文将讨论示例程序RoundedPoint&#xff0c;该程序绘制了圆…

Tomcat 开启远程调试

Tomcat 部署的 war包工程开启远程调试 Linux服务器下&#xff0c;编辑Tomcat bin 目录下的 startup.sh 文件 vim startup.sh在第一行加入&#xff1a;(不换行&#xff0c;在同一行) declare -x CATALINA_OPTS"-server -Xdebug -Xnoagent -Djava.compilerNONE -Xrunjdwp:…

vue3+eleement plus日历选择季度

<template><div class"el-quarter-wrap"><el-popover width"280" v-model"visible"><template #reference><el-input v-model"quarterDate" placeholder"请选择季度" clearable :prefix-icon&qu…

[old]TeamDev DotNetBrowser Crack

TeamDev DotNetBrowser将 Chromium Web 浏览器添加到您的 .NET 应用程序中。在 WPF 和 WinForms 中显示现代网页。使用 DOM、JS、网络、打印等。在 Windows x86/x64/ARM64、macOS x64/Apple Silicon、Linux x64/ARM64 上运行&#xff0c;支持.NET Framework 4.5 特征 HTML5、C…