[C++]set和map的介绍及使用

news/2024/9/18 14:53:29/ 标签: c++, 开发语言, map, set, STL

        关于setmap的接口函数部分,只重点介绍一些相较于别的容器有特殊地方的接口,setmap的接口可以触类旁通。

一、概念

(一)、关联式容器

        关联式容器存储的元素是一个个的键值对<key,value>通过键(key)可以快速索引到其对应的value值。关联容器可以快速查找、读取或者删除所存储的元素,同时该类型的容器插入元素的效率比序列容器(如vector)高。

(二)、键值对

        键值对用来表示具有一一对应关系的一种结构,该结构中只包含两个成员变量key和value,key代表键值,value表示与key对应的信息,可以通过键值key快速索引到其对应的信息value。比如说英汉词典,查询单词的中文释义就需要通过英文单词快速索引到中文释义,这里的键key是英文单词,key的类型为string,key对应的信息value是中文释义,value的类型也为string,这样,英文单词和中文释义就构成了一个<string,string>的键值对。

        键值对可以用pair类来表示,pair是C++的STL提供的一个类。pair - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/utility/pair/        pair存储了公有的两个成员变量,一个叫做first,一个叫做second,可以在外部访问,这两个成员变量的类型通过用户指定。我们可以用pair的first存储键key,用second存储key对应的信息value。

(三)、关联式容器的结构

        根据应用场景的不同,STL总共实现了两种不同结构的关联式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:mapset、multimap、multiset这四种容器的共同点是:使用平衡二叉搜索树(红黑树)作为其底层结果,容器中的元素是一个有序的序列,迭代器遍历得到的是一个有序的序列。

二、set和multiset

(一)set

set - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/set/set/

set的简介

模板类型

template <

           class T,                                        // set::key_type/value_type

           class Compare = less<T>,         // set::key_compare/value_compare

           class Alloc = allocator<T>         // set::allocator_type

               >

T:set存储的元素类型。

Compare:仿函数,用于提供元素之间的比较方法。

Alloc:空间配置器

1、set是按照特定次序存储的没有重复元素的容器。

2、在set,一个元素value就对应其本身的值(value本身就是键key,类型为T),并且每个元素在set必须是不能重复的set中元素的值在容器中不能被再次修改(元素的类型总是const类型),但是可以从容器中插入或删除。

3、在内部,set中的元素总是按照其内部比较对象(类型为Compare)所指示的特定严格弱排序标准进行排序。

4、set容器在通过key访问单个元素时通常比unordered_set容器慢,但set迭代到的序列是有序的。

5、set通常以平衡二叉搜索树的形式实现。

set的接口

1、插入insert

        对于set的insert,第一个(single element)和第三个(range)版本的插入最常用,而中间的第二个(with hint)版本不是很常用,这个接口并不是在指定位置插入,而是在插入时提供一个接近最终插入的位置的迭代器,对插入元素时的效率进行优化,但这种优化效果并不总是显著的,因为用户提示不好,则会忽略用户的提示从头开始搜索插入位置进行插入,且红黑树的平衡性会影响搜索提示时的效率。

        注意到第一个(single element)版本的insert的返回值不是指向新插入位置的迭代器,而是pair<iterator,bool>类型。因为set中的元素都是唯一的,set不允许插入已经存在的元素,若插入的元素存在时,则插入失败,将返回一个pair<iterator,bool>的对象,其first成员为指向set中现有元素位置的迭代器,second成员bool值为false表示插入失败若插入的元素不存在,其first成员为指向新插入元素位置的迭代器,second成员bool值为true,表示插入成功。

代码例子:

#include <iostream>
#include <set>
using namespace std;int main()
{set<int> s;int arr[] = { 2,3,5,6,7,1,2,0,5};//将arr的元素依次插入到s中for (auto e : arr){auto ret = s.insert(e);if (ret.second == false)//插入失败{cout << *(ret.first) << "已存在,插入失败。" << endl;}}//打印s的元素for (auto e : s){cout << e << " ";}return 0;
}

运行结果: 

2已存在,插入失败。
5已存在,插入失败。
0 1 2 3 5 6 7

2、查找并计数count
        这个函数的功能是在容器中搜索与val相等的元素,并返回匹配次数。因为每个元素在set中都是唯一的,所以返回值只能为0和1。如果只想查找某个元素在不在set容器内,可以考虑不使用find函数而使用count函数,如果返回值为1,那么存在,为0则不存在。

代码例子:

int main()
{set<int> s;int arr[] = {1,2,3,4,5,6,7,8,9,10};for (auto e : arr){s.insert(e);}//查找某个值在不在s中int x;//方法1:使用find函数cin >> x;auto it = s.find(x);if (it != s.end())cout << *it << "存在!" << endl;elsecout << x << "不存在" << endl;cout << endl;//方法2,使用count函数cin >> x;if (s.count(x))cout << x << "存在" << endl;elsecout << x << "不存在" << endl;return 0;
}
3、lower_bound和upper_bound

lower_bound:得到下界

        指定一个值x,返回一个迭代器,该迭代器指向容器中的第一个满足条件的元素,条件是该元素大于或等于x。如果使用默认比较类型 (less) 实例化 set 类,则该函数将返回一个迭代器,该迭代器指向的值不小于x。

upper_bound:得到上界

        指定一个值y,返回一个迭代器,该迭代器指向容器中的第一个满足条件的元素,条件是该元素大于y。如果使用默认比较类型 (less) 实例化 set 类,则该函数将返回一个迭代器,该迭代器指向的值大于y。

        通过上面两个函数可以获得到两个迭代器,当x < y 时,这两个迭代器维护一个区间,区间范围是[x,y),左闭右开,可以用于删除指定范围的子序列。

代码例子:

int main()
{std::set<int> myset;std::set<int>::iterator itlow, itup;for (int i = 1; i < 10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90//删除区间位于 [25,65) 的元素 itlow = myset.lower_bound(25);     //找到第一个大于等于25的元素,这里是30itup = myset.upper_bound(65);      //找到第一个大于65的元素,这里是70 //删除对应迭代器区间的元素[itlow,itup)myset.erase(itlow, itup);                     // 10 20 70 80 90std::cout << "myset contains:";for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
}

运行结果:

myset contains: 10 20 70 80 90
 

 4、equal_range

        功能:指定一个值val,返回一对迭代器,该迭代器维护着一段区间,该区间内的元素的值都为相同,为val。由于set的元素是唯一的,所以该区间内最多只能有一个元素。若val值不存在,两个迭代器都指向位于val之后的第一个元素。

代码例子:

int main()
{std::set<int> myset;for (int i = 1; i <= 5; i++) myset.insert(i * 10);   // myset: 10 20 30 40 50std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;ret = myset.equal_range(30);std::cout << "the lower bound points to: " << *ret.first << '\n';std::cout << "the upper bound points to: " << *ret.second << '\n';return 0;
}

运行结果: 

the lower bound points to: 30
the upper bound points to: 40

 

(二)multiset

multiset - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/set/multiset/        multiset功能和set相似,但是multiset允许元素有冗余,可以存储多个相同的元素,而set里的每个元素都是唯一的。这里直接介绍multisetset使用上的不同。

1、insert

        因为multise允许有重复的数据,所以用insert插入数据时不会存在插入失败的情况,其返回值不是pair<iterator,bool>类型,而直接就是iterator类型。

2、erase

        如果通过erase删除值为value的元素,那么在容器中所有与value相等的元素都会被删除。

代码例子:

int main()
{multiset<int> myMultiset;int arr[] = { 10,10,20,20,20,30,40,50,60 };for (auto e : arr){myMultiset.insert(e);}for (auto e : myMultiset){cout << e << " ";}cout << endl;cout << "删除20" << endl;myMultiset.erase(20);for (auto e : myMultiset){cout << e << " ";}return 0;
}

运行结果: 

10 10 20 20 20 30 40 50 60
删除20
10 10 30 40 50 60

3、count 

        由于元素在multise中不再唯一,count的返回值是大于等于0的。

int main()
{multiset<int> myMultiset;int arr[] = { 10,10,20,20,20,30,40,50,60 };for (auto e : arr){myMultiset.insert(e);}for (auto e : myMultiset){cout << e << " ";}cout << endl;cout << "20出现了" << myMultiset.count(20) << "次" << endl;return 0;
}

运行结果: 

10 10 20 20 20 30 40 50 60
20出现了3次
 

4、lower_bound和upper_bound

        功能和上面介绍的set的lower_bound和upper_bound一样,不过lower_bound和upper_bound在multiset用得更多一些。

代码例子:

int main()
{multiset<int> myMultiset;int arr[] = { 10,10,20,20,20,30,40,50,60,60,60,70,80,90,100};//插入数据for (auto e : arr){myMultiset.insert(e);}//打印for (auto e : myMultiset){cout << e << " ";}cout << endl;//删除区间位于 [25,65) 的元素 auto itlow = myMultiset.lower_bound(25);     //找到第一个大于等于25的元素,这里是30auto itup = myMultiset.upper_bound(65);      //找到第一个大于65的元素,这里是70 myMultiset.erase(itlow, itup);for (auto e : myMultiset){cout << e << " ";}return 0;
}

运行结果:

10 10 20 20 20 30 40 50 60 60 60 70 80 90 100
10 10 20 20 20 70 80 90 100

 5、equal_range

        功能和上面介绍的set的equal_range一样,不过equal_range在multiset用得更多一些,可以得到一个迭代器区间,该区间内的元素都和指定key相同。

代码例子:

int main()
{multiset<int> myMultiset;int arr[] = { 10,10,20,20,20,30,40,50,60,60,60,70,80,90,100 };//插入数据for (auto e : arr){myMultiset.insert(e);}//打印for (auto e : myMultiset){cout << e << " ";}cout << endl;//得到重复元素60的迭代器区间pair<multiset<int>::iterator, multiset<int>::iterator> ret = myMultiset.equal_range(60);//删除myMultiset.erase(ret.first, ret.second);//打印for (auto e : myMultiset){cout << e << " ";}cout << endl;return 0;
}

运行结果:

10 10 20 20 20 30 40 50 60 60 60 70 80 90 100
10 10 20 20 20 30 40 50 70 80 90 100

三、map和multimap

(一)map

map - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/map/map/

map的简介

模板类型

template <

           class Key,                                     // map::key_type

           class T,                                       // map::mapped_type

           class Compare = less<Key>,                     // map::key_compare

           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type

           >

key:键key的类型。

T:映射值value的类型。

Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)

Alloc:空间配置器

1、map是一种关联容器,用于存储由键key和映射值value组合而成的元素,按照特定顺序存储。

2、在map中,键值通常用于对元素进行排序和唯一标识,而映射值存储与此键相关的内容。键和映射值的类型可以不同,键和映射值在成员中用一个value_type类型的变量组合在一起,value_type是将两者组合在一起的pair类型:typedef pair<const Key, T> value_type;

3、在内部,映射中的元素总是按照其内部比较对象(类型为Compare)所指示的特定严格弱排序标准的键进行排序。

4、map容器在通过键key访问单个元素时通常比unordered_map容器慢,但map迭代到的序列是有序的。

5、map中的映射值value可以使用括号操作符((operator[])通过对应的键key直接访问到。

6、map通常以二叉搜索树的形式实现。

map的接口 

1、插入insert

        map存储的元素类型是pair<K,V>类型,插入元素时需要传入pair<K,V>类型的对象进行插入。

插入例子:

void test1()
{//英汉词典map<string, string> dict;//插入方法1:实例化pair对象插入pair<string, string> kv1("abandon", "v.放弃");dict.insert(kv1);//插入方法2:构造函数生成匿名对象dict.insert(pair<string, string>("ability", "n. 能力; 才能"));//插入方法3:使用make_pair函数返回pair类型变量dict.insert(make_pair("able", "a. 能够;有能力的"));//插入方法4:多参数构造函数的类型转换pair<string, string> kv2 = { "abnormal"," a. 不正常的" };dict.insert(kv2);dict.insert({ "abnormal"," a. 不正常的" });//插入方法5: 初始化列表initializer_list + 多参数构造函数的类型转换initializer_list<pair<const string, string>> l = { {"aboard","ad.在船、飞机上"} ,{"abolish","v.废除"},{"about","ad. 大约;到处;四处"} };dict.insert(l);dict.insert({ {"aboard","ad.在船、飞机上"} ,{"abolish","v.废除"},{"about","ad. 大约;到处;四处"} });//打印词库for (auto& e : dict){cout << e.first << " -> " << e.second << endl;}
}void test2()
{//插入方法5: 初始化列表initializer_list + 多参数构造函数的类型转换//一步到位//英汉词典map<string, string> dict = { {"abandon", "v.放弃"},{"ability", "n. 能力; 才能"},{"abnormal"," a. 不正常的"},{"aboard","ad.在船、飞机上"},{"abolish","v.废除"},{"about","ad. 大约;到处;四处"}};//打印词库for (auto& e : dict){cout << e.first << " -> " << e.second << endl;}
}int main()
{test1();cout << endl;test2();return 0;
}

运行结果:

abandon -> v.放弃
ability -> n. 能力; 才能
able -> a. 能够;有能力的
abnormal ->  a. 不正常的
aboard -> ad.在船、飞机上
abolish -> v.废除
about -> ad. 大约;到处;四处

abandon -> v.放弃
ability -> n. 能力; 才能
abnormal ->  a. 不正常的
aboard -> ad.在船、飞机上
abolish -> v.废除
about -> ad. 大约;到处;四处
 

2、下标访问operator[]和at

        功能:使map可以像数组通过下标访问到数据一样,通过对应的键key访问到其对应映射的值value。

        如果key与容器中元素的键匹配,则该函数将返回对其映射值的引用。
        如果key与容器中任何元素的键不匹配,则该函数将插入一个该键的新元素,并返回对其映射值的引用。请注意, 本操作会让容器大小增加 1,即使没有为该元素分配映射值来初始化(该新元素会在构造时使用映射值类型的默认构造进行初始化),

        功能:和operator[]功能类似,通过键key访问其映射值,返回key对应的映射值的引用。但是,如果键key不存在,则会抛异常。 

代码例子:

int main()
{//统计选民的投票次数string arr[] = { "张三","李四","张三","李四","王某","王某","李四","李四","王某" };//键:名字 映射值:投票次数map<string, int> count;//计数for (auto& e : arr){//通过名字索引对应的投票次数并加一,代表投票次数加一//如果该名字不在count里,则用该名字作为key新插入一个元素,其映射值使用的是int内置类型的默认构造,其值为0count[e]++; }//打印选民和对应的投票次数for (auto e : count){cout << e.first << " -> " << e.second << endl;}return 0;
}

运行结果:

李四 -> 4
王某 -> 3
张三 -> 2
 

3、其他接口

        map的count、lower_bound、upper_bound以及equal_range的功能和set的是一样的,其功能在set部分已经着重介绍过了,这里不再赘述。 

(二)、multimap

        multimapmap功能类似,这里也不再赘述。需要注意的是multimap允许元素冗余,容器中可以存在多个相同key的元素,并且multimap中没有重载operator[]和at操作,因为multimap中可以有多个相同的key对应不同的映射值,使用operator[]的话返回值的类型应该是一个引用映射值的数组,这样的话会使内存更加复杂化,如果需要得到所有相同key的元素,可以通过equal_range得到这个集合的迭代器区间,这样更加简洁。


http://www.ppmy.cn/news/1517053.html

相关文章

MATLAB 生成指定范围、角度、厚度的含噪平面点云(77)

模拟生成点云并可视化显示,可以验证算法有效性,尤其是针对验证算法的某方面 MATLAB 生成指定范围、角度、厚度的含噪平面点云(77) 一、算法介绍二、使用步骤1.代码2.效果一、算法介绍 如题,模拟生成一组平面点云,含有噪声点,确定算法稳定性,可以指定生成平面的范围,厚…

Java集合—Map系列集合(习题一)

文章目录 Java集合—Map集合&#xff08;习题&#xff09;1.使用泛型修改根据学员姓名找学员对象2.运用Map的三种遍历方式进行遍历迭代器遍历键值对遍历增强遍历 综合要求 Java集合—Map集合&#xff08;习题&#xff09; 1.使用泛型修改根据学员姓名找学员对象 2.运用Map的三…

【JAVA]DAY 2在网页中输出日期和时间,实时还是静止?

一、如何输出日期文本 使用document.write(Date()); 会在网页中输出当前的日期和时间。在 2024 年 8 月 28 日星期三执行这段代码&#xff0c;可能会输出类似 “Wed Aug 28 2024 [具体时间]” 这样的内容。 Date()是 JavaScript 中的一个内置对象&#xff0c;用于处理日期和…

UE5 多个类选择界面生成

在Unreal Engine 5 (UE5) 中&#xff0c;如果你想要创建一个可以选择多个类的界面&#xff0c;你可以使用SClassPicker小部件。以下是一个简单的例子&#xff0c;展示如何在UE5的编辑器模块中创建一个自定义的编辑器工具栏按钮&#xff0c;并打开一个类选择器。 #include &quo…

论文写作遇到的问题——个人记录用

1.实验结果图绘制 python画图字体设置 Science Plots使用中中文配置的问题 11种 Matplotlib 科研论文图表教程 2.论文写作格式 word公式居中、编号右对齐、自动编号、交叉引用 mathtype操作合集&#xff0c;使用大全 arxiv.org的文章引用格式 LaTex的下载与安装&#x…

[CLIP-VIT-L + Qwen] 多模态大模型源码阅读 - DataSet篇

[CLIP-VIT-L Qwen] 多模态大模型源码阅读 - DataSet篇 前情提要源码解读完整代码逐行解读导包readjson函数data_collate函数ImageCaptionDataset类&#xff08;init函数&#xff09;ImageCaptionDataset类&#xff08;readImage函数&#xff09; 参考repo:WatchTower-Liu/VLM-…

Java中Objecy类

没有成员变量 也就只有无参 的构造方法 /*** ClassName Test* author gyf* Date 2024/8/28 10:32* Version V1.0* Description : */ public class Test {public static void main(String[] args) {// toString()Object object new Object();System.out.println(object);String…

网络安全新视角:人工智能在防御中的最新应用

人工智能在网络安全中的最新应用 概述 人工智能&#xff08;AI&#xff09;在网络安全领域的应用正日益成熟&#xff0c;它通过机器学习和深度学习技术&#xff0c;为网络安全带来了革命性的变革。AI技术不仅能够自动化、智能化地检测、分析和应对安全威胁&#xff0c;还能够…

Jenkins:自动化的魔法师,打造无缝CI/CD流水线

标题&#xff1a;“Jenkins&#xff1a;自动化的魔法师&#xff0c;打造无缝CI/CD流水线” 在当今快速发展的软件开发领域&#xff0c;持续集成&#xff08;Continuous Integration, CI&#xff09;和持续部署&#xff08;Continuous Deployment, CD&#xff09;已经成为提升开…

Docker续1:

一、打包传输 1.打包 [rootlocalhost ~]# systemctl start docker [rootlocalhost ~]# docker save -o centos.tar centos:latest [rootlocalhost ~]# ls anaconda-ks.cfg centos.tar 2.传输 [rootlocalhost ~]# scp centos.tar root192.168.1.100:/root 3.删除镜像 [r…

总结:Python语法

Python中的字典、列表和数组是三种常用的数据结构&#xff0c;它们各自有不同的用途和特性。 字典&#xff08;Dictionary&#xff09; 字典是一种无序的、可变的数据结构&#xff0c;它存储键值对&#xff08;key-value pairs&#xff09;。字典中的每个元素都是一个键值对&…

flink--会话模式与应用模式

flink-会话模式部署 会话情况&#xff1a; 添加依赖 <properties><flink.version>1.17.2</flink.version> </properties> ​ <dependencies><dependency><groupId>org.apache.flink</groupId><artifactId>flink-strea…

CSS属性

一、CSS列表样式 1、list-style-type属性&#xff08;列表项标记&#xff09; CSS列表属性允许我们设置不同的列表项标记。 在HTML中&#xff0c;有​两种类型​的列表&#xff1a; ​无序列表​&#xff08;<ul>&#xff09; - 列表项目用​项目符号​标记​有序列表…

【Linux】自动化构建工具makefile

目录 背景 makefile简单编写 .PHONY makefile中常用选项 makefile的自动推导 背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力 ​ ◉ 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…

开放式耳机怎么戴?佩戴舒适在线的几款开放式耳机分享

开放式耳机的佩戴方式与传统的入耳式耳机有所不同&#xff0c;它采用了一种挂耳式的设计&#xff0c;提供了一种新颖的佩戴体验&#xff0c;以下是开放式耳机的佩戴方式。 1. 开箱及外观&#xff1a;首先&#xff0c;从包装盒中取出耳机及其配件&#xff0c;包括耳机本体、充电…

使用 FinalShell 链接 Centos

1. 安装 FinalShell 下载地址&#xff1a;https://www.hostbuf.com/t/988.html 2. 查看 IP地址。 2.1 通过命令查询IP 输入 ip addr show 查询&#xff0c;输出效果如下截图&#xff0c;其中的 192.168.1.5 就是 IP 地址。 2.2 通过可视化界面查询IP 点击右上角的网络图标…

LoadBalancer负载均衡

一、概述 1.1、Ribbon目前也进入维护模式 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 简单的说&#xff0c;Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的…

企业中需要哪些告警Rules

文章目录 企业中需要哪些告警Rules前言定义告警规则企业中的告警rulesNode.rulesprometheus.ruleswebsite.rulespod.rulesvolume.rulesprocess.rules 总结 企业中需要哪些告警Rules 前言 Prometheus中的告警规则允许你基于PromQL表达式定义告警触发条件&#xff0c;Prometheus…

poi word 添加水印

poi word 添加水印 依赖DocxUtil调用遇到的问题部分客户给的word无法添加水印水印文案 过长会导致字变小变形 超过一定长度就会显示异常。消失等情况 依赖 <!--poi-tl--><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</art…

捕获神经网络的精髓:深入探索PyTorch的torch.jit.trace方法

标题&#xff1a;捕获神经网络的精髓&#xff1a;深入探索PyTorch的torch.jit.trace方法 在深度学习领域&#xff0c;模型的部署和优化是至关重要的环节。PyTorch作为最受欢迎的深度学习框架之一&#xff0c;提供了多种工具来帮助开发者优化和部署模型。torch.jit.trace是PyTo…