C++ set,multiset与map,multimap的基本使用

1. 序列式容器和关联式容器

string、vector、list、deque、array、forward_list等STL容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间一般没有紧密的关联关系,比如交换一下,他依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。

关联容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/set系列和unordered_map/unordered_set系列。

map和set底层是红黑树,红黑树是一颗平衡二叉搜索树。set是我们上一篇讲的key的搜索场景的结构,map是key/value搜索场景的结构。

2. set类的介绍

set类的声明如下代码所示

template<class T,class Compare = less<T>,class Alloc = allocator<T>>class set;
  • T是set底层关键字的类型。
  • set默认要求T支持小于比较,如果不支持或者想按自己的需求走可以自行实现仿函数来传给第二个模板参数。
  • set底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第三个参数。

 一般情况下我们都不需要传后两个模板参数。

set底层使用红黑树实现,增删查的效率是O(logN) ,迭代器遍历是走的搜索树的中序,所以是有序的

(1). 构造和迭代器

set的构造我们关注以下几个接口即可。

set的迭代器支持正向和反向迭代遍历,遍历默认按升序顺序,因为底层是二叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,set的iterator和const_iterator都不支持迭代器修改数据,修改关键字数据,破坏了底层搜索树的结构

构造函数的声明

//无参默认构造
explicit set (const key_compare& comp =key_compare(), const allocator_type& alloc = allocator_type() );//迭代器区间构造
template <class InputIterator>
set(InputIterator first , InputIterator last , const key_compare& comp = key_compare() , const allocator_type& = allocator_type());//拷贝构造
set (const set& x);//列表构造
set (initializer_list<value_type> il , const key_compare& comp = key_compare() , const allocator_type& alloc = allocator_type());

C++提供了关键字explicit,用来阻止不应该允许的经过转换构造函数进行的隐式类型转换的发生,声明为explicit的构造函数不能在隐式转换中使用。即只能被明确调用

set的迭代器是一个双向迭代器 bidirectional iterator

//正向迭代器
iterator begin();
iterator end();//反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();
(2). 增删查
// 单个数据插⼊,如果已经存在则插⼊失败 
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊ 
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊ 
template <class InputIterator>
void insert (InputIterator first, InputIterator last);// 查找val,返回val所在的迭代器,没有找到返回end() 
iterator find (const value_type& val);
// 查找val,返回Val的个数 
size_type count (const value_type& val) const;// 删除⼀个迭代器位置的值 
iterator erase (const_iterator position);
// 删除val,val不存在返回0,存在返回1 
size_type erase (const value_type& val);
// 删除⼀段迭代器区间的值 
iterator erase (const_iterator first, const_iterator last);// 返回⼤于等val位置的迭代器 
iterator lower_bound (const value_type& val) const;
// 返回⼤于val位置的迭代器 
iterator upper_bound (const value_type& val) const;

insert插入和迭代器遍历使用代码如下

#include<iostream>
#include<set>
using namespace std;int main()
{set<int> sl;set<int, greater<int>> sg;sl.insert(5);sl.insert(3);sl.insert(8);sl.insert(3);sl.insert(5);sg.insert(5);sg.insert(3);sg.insert(8);set<int>::iterator itl = sl.begin();set<int>::iterator itg = sg.begin();while (itl != sl.end()){cout << *itl << "  ";itl++;}cout << endl;while (itg != sg.end()){cout << *itg << "  ";itg++;}sl.insert({ 2,8,3,9 });cout << endl;for (auto e : sl){cout << e << "  ";}cout << endl;set<string> strs = { "s","ii","affg" };for (auto e : strs){cout << e << "  ";}return 0;
}

输出结果为

 find查找(库里的和set自身的)和erase删除使用代码

#include<iostream>
#include<set>
using namespace std;int main()
{set<int> s = { 4,3,8,3,9,6,10 };for (auto e : s){cout << e << "  ";}cout << endl;//删除最小值s.erase(s.begin());for (auto e : s){cout << e << "  ";}cout << endl;//直接删除一个数xint x;cin >> x;int n = s.erase(x);if (n == 0){cout << x << "这个数不存在!" << endl;}for (auto e : s){cout << e << "  ";}cout << endl;//直接查找在利用迭代器删除xcin >> x;auto pos = s.find(x);if (pos != s.end()){s.erase(pos);}else{cout << x << "不存在!" << endl;}for (auto e : s){cout << e << "  ";}cout << endl;//算法库的查找O(N)auto pos1 = find(s.begin(), s.end(), x);//set自身实现的查找O(logN)auto pos2 = s.find(x);//利用count间接实现快速查找cin >> x;if (s.count(x)){cout << x << "在!" << endl;}else{cout << x << "不存在!" << endl;}return 0;}

输出结果为(6,1,2是我们选择删除的数x)

 通过lower_bound查找大于等于x1的迭代器,在通过upper_bound查找小于x2位置的迭代器

来删除这一段区间,代码如下

#include<iostream>
#include<set>
using namespace std;
int main()
{std::set<int> myset;for (int i = 1; i < 10; i++)myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90for (auto e : myset){cout << e << " ";}cout << endl;// 实现查找到的[itlow,itup)包含[30, 60]区间 // 返回 >= 30 auto itlow = myset.lower_bound(30);// 返回 > 60 auto itup = myset.upper_bound(60);// 删除这段区间的值 myset.erase(itlow, itup);for (auto e : myset){cout << e << " ";}cout << endl;return 0;
}

输出结果如下

3. multiset与set的差异

multiset和set的使用基本完全类似,主要区别点在于multiset支持值冗余,那么insert/find/count/erase都围绕着支持值冗余有所差异

  1. 相较于set,multiset是排序,但是不去重
  2. 相比set不同的是,x可能会存在多个,find查找中序遍历的第一个
  3. 与set不同的是,multiset的count会返回x的实际个数
  4. 相比set不同的是,multiseterase给值时会删除所有的为x的节点

 4. map类的介绍

map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,set默认要求Key支持小于,如果不支持或者需要的话可以自行实现仿函数传给第⼆个模版参数,map底层存储数据的 内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数。map底层是用红⿊树实现增删查改效率是O(logN) ,迭代器遍历是走的中序,所以是按key有序顺序遍历的。

template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare = less<Key>, // map::key_compareclass Alloc = allocator<pair<const Key,T> > > class map;

5. pair类型

map底层的红黑树节点中的数据,使用pair<Key,T>存储键值对数据。

通过 first与second来输出存储的键值

#include<iostream>
#include<set>
using namespace std;
int main()
{pair<int, int> p{ 1,56 };cout << p.first << endl;cout << p.second << endl;return 0;
}

6. map的构造

map的支持正向和反向迭代遍历,遍历默认按key的升序顺序,因为底层是⼆叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,map支持修改value数据,不支持修改key数据,修改关键字数据,破坏了底层搜索树的结构。

//⽆参默认构造 
explicit map (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
//迭代器区间构造 
template <class InputIterator>map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());
// 拷⻉构造 
map (const map& x);
// initializer 列表构造 
map (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());

迭代器依然是一个双向迭代器与set类似

7. map的增删查

map增接口,插入的pair键值对数据,跟set所有不同,但是查和删的接口只用关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value,同时通过迭代还可以修改value。

// 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败 
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊ 
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊ 
template <class InputIterator>
void insert (InputIterator first, InputIterator last);// 查找k,返回k所在的迭代器,没有找到返回end() 
iterator find (const key_type& k);
// 查找k,返回k的个数 
size_type count (const key_type& k) const;// 删除⼀个迭代器位置的值 
iterator erase (const_iterator position);
// 删除k,k存在返回0,存在返回1 
size_type erase (const key_type& k);
// 删除⼀段迭代器区间的值 
iterator erase (const_iterator first, const_iterator last);// 返回⼤于等k位置的迭代器 
iterator lower_bound (const key_type& k);
// 返回⼤于k位置的迭代器 
const_iterator lower_bound (const key_type& k) const;

基本使用

#include<iostream>
#include<set>
#include<map>
using namespace std;
int main()
{map<string, string> dict = { {"dog","狗"}, {"cat","猫"},{"pig","猪"} };//map<string, string>::iterator it = dict.begin();auto it = dict.begin();while (it != dict.end()){//cout << (*it).first <<":"<<(*it).second << endl;// map的迭代基本都使⽤operator->,这⾥省略了⼀个-> // 第⼀个->是迭代器运算符重载,返回pair*,第⼆个箭头是结构指针解引⽤取pair数据//cout << it.operator->()->first << ":" << it.operator->()-> second << endl;cout << it->first << ":" << it->second << endl;++it;}cout << endl;// insert插⼊pair对象的4种⽅式,对⽐之下,最后⼀种最⽅便 pair<string, string> kv1("first", "第一个");dict.insert(kv1);dict.insert(pair<string, string>("second", "第二个"));dict.insert(make_pair("sort", "排序"));dict.insert({ "auto", "自动的" });// "left"已经存在,插⼊失败 dict.insert({ "left", "左边" });// 范围for遍历 for (const auto& e : dict){cout << e.first << ":" << e.second << endl;}cout << endl;string str;while (cin >> str){auto ret = dict.find(str);if (ret != dict.end()){cout << "->" << ret->second << endl;}else{cout << "无此单词,请重新输入" << endl;}}return 0;
}

8. map的数据修改

map支持修改mapped_type数据,不支持修改key数据,因为这样会修改关键字数据,从而破坏了底层搜索树的结构。

map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map 还有⼀个⾮常重要的修改接口operator[],但是operator[]不仅仅支持修改,还⽀持插⼊数据和查找数据,所以他是⼀个多功能复合接口

需要注意从内部实现⻆度,map这⾥把我们传统说的value值,给的是T类型,typedef为 mapped_type。而value_type是红⿊树结点中存储的pair键值对值。⽇常使⽤我们还是习惯将这⾥的 T映射值叫做value。

insert插入一个pair<key , T>对象

1. 如果key已经在map中,插入失败,则返回一个pair<iterator,bool>对象,返回pair对象first是新插入key所在节点的迭代器,second是false。

2. 如果key不在map中,插入成功,也返回一个pair<iterator,bool>对象,返回pair对象first是新插入key所在节点的迭代器,second是true。

也就是说无论插入成功与否,返回pair<iterator,bool>对象的first都会指向key所在的迭代器

那么就意味着insert插入失败时充当了查找的功能,正是因为这一点,insert可以用来实现operator[]。

需要注意的是这里有两个pair,一个是map底层红黑树节点中存储的pair<key,T>,另一个是insert的返回值pair<iterator,bool>

#include<iostream>
#include<algorithm>
#include<iostream>
#include<map>using namespace std;int main()
{string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "葡萄","李","梨" };map<string, int> CountMap1;map<string, int> CountMap2;for (const auto& str : arr){auto val = CountMap1.find(str);//看是否在map中有str水果if (val == CountMap1.end()){CountMap1.insert({ str,1 });//不在就插入水果,将次数设置为1}else{val->second++;//在就将查到的节点中的水果对应val值++}}for (const auto& e : CountMap1){cout << e.first << ":" << e.second << endl;}cout << endl;for (const auto& str : arr){auto val = str;//key第一次出现,就插入这个值并将其val++CountMap2[val]++;}for (const auto& e : CountMap2){cout << e.first << ":" << e.second << endl;}cout << endl;return 0;
}

输出结果如下

9. multimap和map的差异

multimap和map的使用基本完全类似,主要区别点在于multimap支持关键值key冗余,insert/find/count/erase也都围绕着支持关键字key冗余有所差异,这里跟set和multiset区别完全一样,比如find返回中序的第一个。其次就是multimap不支持[],因为key支持冗余,[]就只能支持插入,不能支持修改了


这篇就到这里啦ヾ( ̄▽ ̄)Bye~Bye~

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

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

相关文章

reverse--->恶意代码分析(第一次接触)。

学习笔记。 前言&#xff1a;第一次接触&#xff0c;朋友发给我的。 取自&#xff1a;22年信息安全管理与评估二阶段。 要求&#xff1a; 下载 查壳 32ida打开。 先上微步云沙箱看看&#xff1a; 样本报告-微步在线云沙箱 (threatbook.com)https://s.threatbook.com/repor…

css 中 ~ 符号、text-indent、ellipsis、ellipsis-2、text-overflow: ellipsis、::before的使用

1、~的使用直接看代码 <script setup> </script><template><div class"container"><p><a href"javascript:;">纪检委</a><a href"javascript:;">中介为</a><a href"javascript:…

Python批量处理客户明细表格数据,挖掘更大价值

批量处理 .xls 数据并进行归类分析以挖掘内在价值&#xff0c;通常涉及以下步骤&#xff1a; 读取数据&#xff1a;使用 pandas 库读取 .xls 文件。数据清洗&#xff1a;处理缺失值、异常值、重复值等。数据转换&#xff1a;对数据进行必要的转换&#xff0c;如日期格式统一、…

C嘎嘎入门篇:类和对象(2)

前言&#xff1a; 上一篇小编讲了类和对象&#xff08;1&#xff09;&#xff0c;当然&#xff0c;在看这篇文章之前&#xff0c;读者朋友们一定要掌握好前面的基础内容&#xff0c;因为这篇和前面息息相关&#xff0c;废话不多说&#xff0c;下面小编就加快步伐&#xff0c;开…

【JavaEE】http/https 超级详解

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 &#x1f98a;一.定义 HTTP&#xff08;HyperText Transfer Protocol&#xff09;即超文本传输协议&#xff0c;他是应用非常广泛的应用层协议&#xff0c;是…

【CSS in Depth 2 精译_041】6.4 CSS 中的堆叠上下文与 z-index(上)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09;第二章 相对单位&#xff08;已完结&#xff09;第三章 文档流与盒模型&#xff08;已完结&#xff09;第四章 Flexbox 布局&#xff08;已…

TryHackMe 第5天 | Pre Security (四)

该学习路径讲解了网络安全入门的必备技术知识&#xff0c;比如计算机网络、网络协议、Linux命令、Windows设置等内容。过去三篇已经对前三块内容进行了简单介绍&#xff0c;本篇博客将记录 Windows设置 部分。 Windows Fundamentals Part 1 对于 Windows &#xff0c;肯定会感…

(最新已验证)stm32 + 新版 onenet +dht11+esp8266/01s + mqtt物联网(含微信小程序)上报温湿度和控制单片机(保姆级教程)

物联网实践教程&#xff1a;微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 前言 之前在学校获得了一个新玩意&#xff1a;ESP-01sWIFI模块&#xff0c;去搜了一下这个小东西很有玩点&#xff0c;远程控制LED啥的&#xff0c;然后我就想…

【Verilog学习日常】—牛客网刷题—Verilog企业真题—VL69

脉冲同步器&#xff08;快到慢&#xff09; 描述 sig_a 是 clka&#xff08;300M&#xff09;时钟域的一个单时钟脉冲信号&#xff08;高电平持续一个时钟clka周期&#xff09;&#xff0c;请设计脉冲同步电路&#xff0c;将sig_a信号同步到时钟域 clkb&#xff08;100M&…

c语言手撕内存池组件

内存池是什么&#xff1f; 内存池&#xff08;Memory Pool&#xff09;是一种内存管理技术&#xff0c;它预先分配一大块内存&#xff0c;然后将其分成多个固定大小的小块。这些小块被组织起来&#xff0c;用于程序在运行期间频繁进行的内存分配和释放操作。内存池通过创建一个…

大数据实时数仓Hologres(四):基于Flink+Hologres搭建实时数仓

文章目录 基于FlinkHologres搭建实时数仓 一、使用示例 二、方案架构 1、架构优势 2、Hologres核心优势 三、实践场景 四、项目准备 1、创建阿里云账号AccessKey 2、准备MySQL数据源 五、构建实时数仓​编辑 1、管理元数据 2、构建ODS层 2.1、创建CDAS同步作业OD…

GS-SLAM论文阅读笔记--GEVO

前言 这篇文章看着就让人好奇。众所周知&#xff0c;高斯是一个很不错的建图方法&#xff0c;但是本文的题目居然是只用高斯进行单目VO&#xff0c;咱也不知道这是怎么个流程&#xff0c;看了一下作者来自于MIT&#xff0c;说不定是个不错的工作&#xff0c;那就具体看看吧&am…

算法-汉诺塔问题(Hanoi tower)

介绍 汉诺塔是源于印度的一个古老传说的小游戏&#xff0c;简单来说就是有三根柱子&#xff0c;开始的时候&#xff0c;第一根柱子上圆盘由大到小&#xff0c;自下往上排列。这个小游戏要实现的目的呢&#xff0c;就是要把第一根柱子上的圆盘移到第三根的柱子上去&#xff1b;…

部标主动安全(ADAS+DMS)对接说明

1.前言 上一篇介绍了部标&#xff08;JT/T1078&#xff09;流媒体对接说明&#xff0c;这里说一下如何对接主动安全附件服务器。 流媒体的对接主要牵扯到4个方面&#xff1a; &#xff08;1&#xff09;平台端&#xff1a;业务端系统&#xff0c;包含前端呈现界面。 &#x…

企业数字化转型的深层次问题与战略解读——基于TOGAF框架的深入分析与解决方案

数字化转型的必然性与复杂性 随着全球化和技术进步的推动&#xff0c;数字化转型成为企业保持竞争力、提升效率、满足客户需求的重要战略选择。然而&#xff0c;数字化转型并不仅仅是技术的简单引入&#xff0c;它涉及到业务模式、运营流程、组织架构以及企业文化的深刻变革。…

对比学习训练是如何进行的

对比学习&#xff08;Contrastive Learning&#xff09;是一种自监督学习的方法&#xff0c;旨在通过拉近相似样本的表示、拉远不相似样本的表示来学习特征表示。在训练过程中&#xff0c;模型并不依赖标签&#xff0c;而是通过样本之间的相似性进行学习。以下是对比学习的基本…

Another redis desktop manager使用说明

Another redis desktop manager使用说明 概述界面介绍图示说明连接界面设置界面查看操作日志主界面信息进入redis-cli控制台更多 概述 Another Redis Desktop Manager是一个开源的跨平台 Redis 客户端&#xff0c;提供了简洁易用的图形用户界面&#xff08;GUI&#xff09;&am…

C++ 数据结构算法细节相关

细节 队列 这段代码实现的是二叉树的层序遍历&#xff0c;也就是按照树的层次&#xff0c;一层一层地遍历节点。下面我会为你详细解释这段代码。 queue <TreeNode*> q; 这是一个队列&#xff0c;队列中存放的是指向TreeNode的指针。队列&#xff08;queue&#xff09;是…

云原生数据库 PolarDB

简介&#xff1a;云原生数据库 PolarDB 是阿里云自研产品&#xff0c;在存储计算分离架构下&#xff0c;利用了软硬件结合的优势&#xff0c;为用户提供秒级弹性、高性能、海量存储、安全可靠的数据库服务。100%兼容MySQL和PostgreSQL生态&#xff0c;支持分布式扩展&#xff0…

Mybatis总结

Mybatis 概述及搭建 原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了 Google Code&#xff0c;随着开发团队转投GoogleCode 旗下&#xff0c; iBatis3.x正式更名为MyBatis。 MyBatis 是一款优秀的持久层框架。 MyBatis 避免了几乎所有…