【C++】map和set

目录

关联式容器

特性 序列式容器 vector/list 关联式容器 map/set
顺序 插入顺序 key 排序
结构 线性排列 红黑树
查找 O (n) 慢 O (log n) 快
元素 单个值 key/key+value
排序 不会自动排 自动排序
用途 存数据、遍历 查找、映射、去重

序列式容器:map/set

STL总共实现了两种不同结构的管理式容器:树形结构和哈希结构。树形结构的关联式容器主要有四种:map、set、multimap、multiset 。共同点都是用平衡搜索树,即红黑树作为底层结构,容器中的元素是一个有序的序列

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value代表与key对应的信息。如英文单词和其中文含义就是一个一一对应的关系,就可以把英文单词当做key,中文含义当做value。

cpp 复制代码
//SGI 版 STL 中的实现
template <class T1, class T2>
struct pair {
  typedef T1 first_type;
  typedef T2 second_type;

  T1 first;	
  T2 second;	
  pair() : first(T1()), second(T2()) {}
  pair(const T1& a, const T2& b) : first(a), second(b) {}

};

pair中的first表示键值,second表示实值,即一一对应的关系。

构造一个匿名键值对pair对象,可以如下表示:

cpp 复制代码
pair<string, string>{"left","左边"};

库里面写了一个函数模板:make_pair ,作用就是:自动帮你创建一个 pair 对象。

如:

cpp 复制代码
make_pair("right","右边");

map -> key/value搜索树

对map的使用

cpp 复制代码
void Testkeyvalue3()//使用map
{
	map<string, string> dict;
	pair<string, string> kv1("hello","你好" );
	dict.insert(kv1);//insert插入的参数就是pair键值
	dict.insert(pair<string, string>{"left","左边"});//匿名对象
	dict.insert(make_pair("right","右边"));//使用make_pair(),实际是函数模板,即可不用指定传参类型
	//pair<string, string> kv2 = { "apple","苹果" };  多参数的构造函数支持隐式类型转换
    //dict.insert(kv2);这两行就分别是构造+拷贝构造
	dict.insert({ "apple","苹果" });//构造和拷贝构造优化为直接构造

	//会按key的大小进行插入

	//对map的遍历输出
	//auto it1 = dict.begin();
	//while (it1 != dict.end())
	//{
	//	//cout << (*it1).first << "->" << (*it1).second << endl;
	//	cout << it1->first << "->" << it1->second << endl;//常用
	//	it1++;
	//}

	string str;
	while (cin >> str)
	{
		auto s = dict.find(str);
		if (s != dict.end())//不等于相当于找到了str的位置,find找不到时返回的就是end迭代器
		{
			cout << str << "->" << s->second << endl;
		}
		else
		{
			cout << "无此单词,请重新输入" << endl;
		}
	}
}

迭代器重载了 *-> 运算符 ,所以可以直接当成指针来使用。
(*it1).first:*it1是对迭代器的解引用,

● .first:访问这个键值对的键

● .second:访问这个键值对的值
it1->first:-> 是指针访问成员运算符,it1->返回的是operator->,即pair *

相当于:(it1.operator->())->first

operator\[\]

形式:

cpp 复制代码
mapped_type& operator[] (const key_type& k);//即参数值是key,返回值是value

\[\]的功能等价于:

解释一下:

先看insert这块:

mapped_type(),一个缺省值,不写的话相当于调用的是默认构造

insert的格式:

它的返回值是个pair,分为两种情况:

  1. 当map中要插入一个新的key时,此时返回值就是<新插入元素的迭代器,true>
  2. 当map中已经存在一个key时,此时返回值就是<map中该key的迭代器,false>

insert返回的是pair,然后取first,即迭代器,进行解引用,即得到元素的<key,value>,再取second,所以\[\]的返回值就是value。


使用\[\]用于查找时要注意它是否存在,若它原本不存在则会变为插入。

multimap

它的key值可以重复,而map的key不能重复。

multimap中允许出现多个重复的键值,这就意味着operator[]无法确认调用者的意图,也就不知道要返回哪个 键值 对应的 实值。

所以multimap中没有提供 operator[]

set -> key搜索树

set的简单介绍

  1. set是按照一定次序存储元素的容器。
  2. set中的元素不能在容器中修改,元素总是const类型,但是可以从容器中插入或删除它们。
  3. set中的元素总是会按照其内部类型所指示的特定严格弱排序准则进行排序。如对int类型就是升序,对string类型就是按字典序排序。
  4. set中只存放value,但在底层实际存放的是由<value, value>构成的键值对。
  5. set中查找某个元素的时间复杂度为O(log n)

迭代器遍历默认走的是中序遍历,会自动去重。

set的使用

对于set的各种函数,直接看文档即可:set的文档

简单说一下关于删除的:

删除掉最小值:s.erase(s.begin());

cpp 复制代码
for (int i = 1; i < 10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90

// [30, 60]
// >= 30
itlow = myset.lower_bound(30);                //
// > 60
itup = myset.upper_bound(60);                 //               
myset.erase(itlow, itup);                     // 10 20 70 80 90

关于set的使用也很简单:

cpp 复制代码
//前面要加上set的头文件
#include<iostream>
#include<set>
using namespace std;
int main()
{
    int arr[] = {1, 3, 2, 6, 7, 3, 0, 8, 7, 3, 1, 1};
    set<int> si(arr, arr+sizeof(arr)/sizeof(arr[0]));
    cout << si.size() << endl;

    //打印set中的元素
    for(auto& e : si)
        cout << e << " ";
    //结果为:0 1 2 3 6 7 8,发现对其去了重,并进行了排序。
    return 0;
}

multiset

multiset不会去重,在删除时,会把所有相同的值都删除。

multiset和set的区别:

  1. find查找x时,多个x在树中,返回中序第一个x
  2. 插入时,允许相等的值插入

相关OJ题

以下几个题都可以用set或map实现。
138. 随机链表的复制 - 力扣(LeetCode)

  1. 处理深拷贝
  2. 利用map构造原节点和拷贝节点之间的关系

问题点:新旧节点random的映射还有点懵逼。即对\[\]的使用还不熟练。

142. 环形链表 II - 力扣(LeetCode)

349. 两个数组的交集 - 力扣(LeetCode)

法一:把两个数组都放入set中,小的++,相等的同时++;

法二:排序+去重,依次比较,小的++,相等的同时++;

应用:差集进行同步,交集进行比对

692. 前K个高频单词 - 力扣(LeetCode)

坑点:map对key进行排过序了,但是我如果直接用sort对value即次数进行排序,那就会乱,因为sort底层是快排,不稳定,可以用stable_sort,也可以调整仿函数的比较逻辑


感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

相关推荐
xieliyu.12 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
CryptoPP13 小时前
快速对接东京证券交易所API数据:实战指南与代码示例
开发语言·人工智能·windows·python·信息可视化·区块链
ZC跨境爬虫13 小时前
跟着 MDN 学JavaScript day_7:数学运算与逻辑判断实战测试
开发语言·前端·javascript·学习·ecmascript
cfm_291414 小时前
Redis五大基本数据结构底层了解
数据结构·数据库·redis
如竟没有火炬14 小时前
最大矩阵——单调栈
数据结构·python·线性代数·算法·leetcode·矩阵
阳区欠14 小时前
【LangChain】LLM基础介绍
开发语言·python·langchain
Jinkxs14 小时前
Java 跨域14-Java 与区块链(Hyperledger)集成
java·开发语言·区块链
晨曦中的暮雨15 小时前
Golang速通(Javaer版)
java·开发语言·后端·golang
小小编程路16 小时前
Python 还有容器类型互转、进制转换、字符编码转换
开发语言·windows·python
Qt程序员16 小时前
Linux RCU 原理与应用
linux·c++·内核·linux内核·rcu