【C++】对set和map的使用

目录

一、set

[1. 基本介绍](#1. 基本介绍)

[2. set 的使用](#2. set 的使用)

(1)初始化

(2)插入&删除&查找

(3)迭代器

(4)其他常见操作

[3. multiset 与 set](#3. multiset 与 set)

二、map

[1. 基本介绍](#1. 基本介绍)

[2. pair的使用](#2. pair的使用)

[3. map 的使用](#3. map 的使用)

(1)初始化

(2)★插入★

(3)删除

(4)查找

(5)迭代器

[(6)★operator[ ]([ ]重载 )★](#(6)★operator[ ]([ ]重载 )★)

(7)其他常见操作

[4. multimap 与 map](#4. multimap 与 map)


前言

上篇文章我们讲解了二叉搜索树的一些认识,而本篇文章我们将要对C++中的两个容器 setmap进行介绍,掌握它们两个的基本使用。 set 和 map 的底层其实都是一种搜索树,但不是普通的二叉搜索树,而是红黑树(红黑树是在普通二叉搜索树上进行了一些优化了的搜索树),在认识set和map时我们只需要知道它们的通过搜索树实现的就可以了(以上篇文章讲解的普通二叉搜索树来理解当然就够了)。


一、set

set介绍的文档链接:set - C++ Reference

1. 基本介绍

对于set,我们可以将它当做二叉搜索树的一种key模型来看待。set是通过封装红黑树实现的。

set的底层是一棵二叉搜索树(红黑树),所以说在set在存储的数据通过中序遍历就是有序的。可以用来排序

并且set中的数据不能重复,插入已有的数据会失败,因此 set 常常会用来帮助我们对数据去重

在set中的元素不能在容器中修改(set中的元素总是const的),但是我们可以从容器中插入或删除它们。

2. set 的使用

(1)初始化

和其他容器一样,库里面的set也是通过模板实现的,如图所示:

其中模板参数的意思如下:

T: set中存放元素的类型(实际在底层存储的一个键值对,即这里的T就是<Key, Key>,当下可以不用理解太深)。

Compare:set中元素默认按照小于来比较

Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理

所以,一般我们在使用时,只需要传递一个存储数据的类型就可以了,即:

cpp 复制代码
#include<set>
using namespace std;
int main()
{
	set<int> MySet; // 定义一个set对象
	return 0;
}

当我们构造一个set对象时,也可以通过其他方式,如图是库中的构造函数:

这三者方式都比较常见。

(2)插入&删除&查找

对于插入

如图所示:

其中第2、3个都比较好理解。

对于第1个函数的意思是:

在set中插入元素val,实际插入的是 <val, val> 构成的键值对,

如果插入成功,返回 <该元素在set中的位置(迭代器),true> ;

如果插入失败,说明 val 在set中已经 存在,返回 <在set中的位置(迭代器),false>

对于删除

如图所示:

第1个的意思:删除set中迭代器position所指位置的元素。

第2个的意思:删除set中值为x的元素,返回删除的元素的个数

第3个的意思:删除set中[first, last)区间中的元素

对于查找

如图所示:

就是查找val,找到后,返回其对应的迭代器;否则,就返回 set::end 。

(3)迭代器

set中可以通过迭代器进行遍历,如图所示:

通过set的迭代器遍历出的元素,是有序的,如以下代码:

cpp 复制代码
#include<iostream>
#include<set>
using namespace std;
int main()
{
	int arr[] = { 2,4,5,6,3,1,7,8,9,10 };
	set<int> MySet(arr,arr + sizeof(arr) / sizeof(arr[0])); //使用数组一个set对象
	set<int>::iterator it = MySet.begin();
	while (it != MySet.end())
	{
		cout << *it << " "; //输出:1 2 3 4 5 6 7 8 9 10
		it++;
	}
	cout << endl;

	//反向迭代器
	set<int>::reverse_iterator rit = MySet.rbegin();
	while (rit != MySet.rend())
	{
		cout << *rit << " "; //输出:10 9 8 7 6 5 4 3 2 1
		rit++;
	}
	cout << endl;
	return 0;
}

(4)其他常见操作

对于count,如图:

作用:返回set中值为val的元素的个数。

一般这个在set中并不起作用,因为在set中的数据都是唯一的,所以在set中 count函数的作用就是查找,如果存在val,就返回1;不存在就返回0。由于set中有find这个查找的函数,所以一般这个函数我们可以认识在set中没有意义。但是在 multiset 就是有意义的。
对于lower_bound,如图:

它的作用:返回一个指向第一个不小于给定值 val 的元素的迭代器。
对于upper_bound,如图:

它的作用:返回一个指向第一个大于给定值 val 的元素的迭代器。
对于clear,如图:

作用是将set中的所有元素清空
还有两个,如图:

empty用来判空

size可以返回set中有效元素的个数

以上就是set的一些常见使用,其他还有一些不常见的,可以才参考set文档::set - C++ Reference了解。

3. multiset 与 set

multiset介绍的文档链接:multiset - C++ Reference

multiset和set都在同一个头文件中<set>中:

multiset的内容和set大部分都是相同的;

与set的区别就是,multiset中存储的元素可以重复,而set是中元素是唯一的。

如以下代码所示:

cpp 复制代码
#include<iostream>
#include<set>
using namespace std;
int main()
{
	int arr[] = { 2,4,5,6,3,3,3,3,3,3,4,4,4,1,7,8,9,10 };

	//set输出
	set<int> MySet(arr, arr + sizeof(arr) / sizeof(arr[0]));
	for (auto& e : MySet)
	{
		cout << e << " "; //输出:1 2 3 4 5 6 7 8 9 10
	}
	cout << endl;

	//multiset输出
	multiset<int> MyMultset(arr, arr + sizeof(arr) / sizeof(arr[0]));
	for(auto& e: MyMultset)
	{
		cout << e << " "; //输出:1 2 3 3 3 3 3 3 4 4 4 4 5 6 7 8 9 10
	}
	cout << endl;
	return 0;
}

二、map

map文档介绍链接:map - C++ Reference

1. 基本介绍

map也是通过封装二叉搜索树(红黑树)实现的。

map相当于二叉搜索树中的Key_Value模型,在map的二叉树中,每一个结点存储的是由键值key和值value组合而成的元素。这个元素是一个pair的对象。

在内部,map中的元素是按照键值key进行比较排序的。

在map中,每一个结点的key与value是绑定在一起,其中key仍然不能被修改(key是const的),而value则可以被修改。

2. pair的使用

在map中每一个节点存储的都是key和value组合的pair对象。下面我们来简单介绍一个pair。

pair的文档链接:pair - C++ Reference

如图:

pair类可以将一对值组合在一起,这两个值可以是不同的类型(T1T2)。它有两个公有成员 first 和 second 。first对应T1,second对应T2

pair的构造函数:

可以构造空的pair,也可以拷贝构造,还可以通过传递两个数据(first_type& a, second_type& b)进行构造。

我们还有一种方法来实现一个pair:

使用make_pair来构造pair。通过传递两个参数给make_pair来返回一个pair也是常见的方法。

3. map 的使用

(1)初始化

如图所示:

这是map的模板参数列表,与set相比map就有两个数据类型。

key: 对应键值对中key的类型

T: 对应键值对中value的类型

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

Alloc:空间配置器。

所以,map的定义就如下:

cpp 复制代码
#include<iostream>
#include<map>
using namespace std;
int main()
{
	map<int, int> MyMap; //定义一个map的对象
	return 0;
}

再看map中的一些变量重定义:

从这里就可以看出来map中存储的就是pair<Key,T>。

map的构造函数:

(2)★插入★

对于插入

如图所示:

首先来看第一个,我们插入的val必须是一个pair对象(因为这里的value_type就是pair重定义而来),所以我们的插入必须要调用pair的构造或者是make_pair函数。这里我们有多种方法,如下所示:

cpp 复制代码
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
	map<string, string> MyMap;
	pair<string, string> kv1("insert", "插入");
	MyMap.insert(kv1);
	MyMap.insert(pair<string, string>("sort", "排序"));//只有转换为pair才行

	// C++98 隐式类型转换:const char* -> string
	MyMap.insert(make_pair("string", "字符串"));

	// C++11 多参数的构造函数隐式类型转换:统一的初始化语法 { }
	MyMap.insert({ "int", "整型" });

	return 0;
}

对于返回值,在map中插入键值对val后,如果插入成功,则返回的pair的first是指向新插入的元素的迭代器,返回值是true;如果插入失败(即元素已经存在),则返回的pair的first是指向已存在这个的元素的迭代器,返回值是false;

然后,对于第二个构造函数在传递val的时候也需要注意要传pair类型的数据。

最后,第三个的迭代器构造,需要注意传递的迭代器是一个指向pair类型的迭代器。

(3)删除

map的删除比较简单,如图:

第1个函数:删除position位置上的元素

第2个函数:删除键值为k的元素,即删除map中pair中first为k的这个pair类型的元素。

第3个函数:删除[first, last)区间中的元素

(4)查找

如图所示:

map的find查找是按照pair中的first进行查找的,将k与pair中的first比较进行查找。找到了就返回该元素所在的迭代器;否则就返回map::end。

(5)迭代器

map迭代器也比较全面,如图所示:

使用也比较简单,唯一需要注意的就是解引用访问时访问的pair类型,而我们需要访问的是pair中的first和second这里个成员变量。这里我们简单使用一下:

cpp 复制代码
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
	map<string, string> MyMap;
	MyMap.insert(make_pair("string", "字符串"));
	MyMap.insert(make_pair("int", "整型"));
	MyMap.insert(make_pair("insert", "插入"));
	MyMap.insert(make_pair("erase", "删除"));
	MyMap.insert(make_pair("sort", "排序"));

	//访问
	map<string, string>::iterator it = MyMap.begin();
	while (it != MyMap.end())
	{
		//两种方式都可以

		//cout << (*it).first << "-->" << (*it).second << endl; //直接解引用访问

		//使用->重载访问,编译器做了优化,本来是(it->)->first,两个->优化为了一个
		cout << it->first << "-->" << it->second << endl; 
		it++;
	}
	cout << endl;

	return 0;
}

(6)★operator[ ]([ ]重载 )★

在map中使用比较复杂的就是operator[ ]这个函数了。它既有插入,也有查找的意思。

首先来看这个函数:

相当于调用:

可以发现这是很复杂的,所以我们先把它分开写,让逻辑清晰一下:

所以,这里的operator[ ] 的功能其实就是:

先通过在map中插入一个 k (键对值的第一个变量类型)。

  • 如果k已经在树里面了,这时的ret就是pair<树里面的k所在节点的迭代器iterator, false>;
  • 如果k不在树里面,这时的ret就是pair<新插入k所在节点的迭代器iterator, false>;

然后再通过ret得到k所在的迭代器,通过对这个迭代器解引用(或者是使用->)就可以找到map中k对应的value,然后在返回,拿到k所在的value,由于这里是传引用返回,那么通过这个operator[ ] 也就可以直接修改map中k对应的value值了。

在一些地方使用 [ ] 也是非常方便的。

使用示例:

图中这两种方法的实现效果都是一样的,运行结果:

(7)其他常见操作

map中也还有许多其他的函数接口,比如:

|------------------------------------------------------------------------|---------------------------------------------|--------------------------------------------------------------------------------|
| 接口 | 函数声明 | 作用 |
| empty | bool empty() const; | 检测map中的元素是否为空 |
| size | size_type size() const; | 返回map中有效元素的个数 |
| swap | void swap (map& x); | 交换两个map中的所有元素 |
| clear | void clear(); | 将map中的元素清空 |
| count | size_type count (const key_type& k) const; | 返key为k的键值在map中的个数,注意 map中key是唯一的,因此该函数的返回值 要么为0,要么为1,因此也可以用该函数来 检测一个key是否在map中 |

以上便是对map的常见接口的认识了。

4. multimap 与 map

multimap介绍的文档链接:multimap - C++ Reference

multimap和map都在头文件<map>中:

multimap和map的接口功能几乎是一样的。

不同的是multimap的多个键值对pair之间的key(即map存储的pair中的first)是可以重复的。而map的不同pair之间的key不能重复。也就是说,在multimap中,key是可以有多个的,而相同的key对应的value是可能不同的。

另外还有一个点:multimap的count函数是可以发挥实际作用的。


感谢各位观看!希望能多多支持!

相关推荐
從南走到北1 小时前
JAVA代驾小程序源码代驾跑腿APP源码
java·开发语言·微信·微信小程序·小程序
快乐zbc1 小时前
C++ 中 typedef 指针别名与 const 的坑
开发语言·c++
urkay-1 小时前
Android getDrawingCache 过时废弃
android·java·开发语言·kotlin·iphone·androidx
小oo呆1 小时前
【学习心得】Python好库推荐——pipx
linux·开发语言·python
CoderYanger1 小时前
C.滑动窗口-求子数组个数-越短越合法——3258. 统计满足 K 约束的子字符串数量 I
java·开发语言·算法·leetcode·1024程序员节
AI科技星2 小时前
时空运动的几何约束:张祥前统一场论中圆柱螺旋运动光速不变性的严格数学证明与物理诠释
服务器·数据结构·人工智能·python·科技·算法·生活
幽络源小助理2 小时前
《已调试》SpringBoot景区寄存管理系统源码 - 免费JavaWeb项目下载 | 幽络源
java·开发语言·spring boot
豆沙沙包?2 小时前
2025年--Lc302-415. 字符串相加--java版
java·开发语言
JIngJaneIL2 小时前
基于Java民宿管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot