C++【set 和 map 学习及使用】

这篇具有很好参考价值的文章主要介绍了C++【set 和 map 学习及使用】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

✨个人主页: 北 海
🎉所属专栏: C++修行之路
🎃操作环境: Visual Studio 2019 版本 16.11.17

C++【set 和 map 学习及使用】



🌇前言

setmapSTL 中的容器之一,不同于普通容器,它俩的查找速度极快,常用来存储各种经常被检索的数据,因为这俩容器的底层是平衡二叉搜索树中的红黑树。除此之外,还可以借助其特殊的性质,解决部分难题

C++【set 和 map 学习及使用】


🏙️正文

1、预备知识

在正式学习 setmap 之前,首先要有一些预备知识,否则后面可能看不懂相关操作

1.1、关联式容器

在以往的 STL 容器学习中,我们接触到的都是 序列式容器,比如 stringvectorlistdeque 等,序列式容器的特点就是 底层为线性序列的数据结构,就比如 list,其中的节点是 线性存储 的,一个节点存储一个元素,其中存储的元素都可序,但未必有序

关联式容器 则比较特殊,其中存储的是 <key, value>键值对,这就意味着可以按照 键值大小 key 以某种特定的规则放置于适当的位置,关联式容器 没有首尾的概念,因此没有头插尾插等相关操作,本文中学习的 setmap 就属于 关联式容器

C++【set 和 map 学习及使用】
出自《STL源码剖析》

注意:stackqueue 等适配器也属于序列式容器,因为他们的底层是 deque 等容器

1.2、键值对

键值对是 一种用来表示具有一一对应关系的结构,该结构中一般只包含两个成员变量:keyvalue,前者表示 键值,后者表示 实值

关联式容器的实现离不开键值对

因此在标准库中,专门提供了这种结构 pair
定义如下

//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) {}

#ifdef __STL_MEMBER_TEMPLATES
  template <class U1, class U2>
  pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
#endif
};

pair 中的 first 表示 键值second 则表示 实值,在给 关联式容器 中插入数据时,可以构建 pair 对象
比如下面就构建了一个 键值 keystring,实值 valueint 的匿名 键值对 pair 对象

pair<string, int>("hehe", 123);

可以将此匿名对象传入 关联式容器 中,当然这样写未免过于麻烦了,于是库中设计了一个函数模板 make_pair,可以根据传入的参数,去调用 pair 构建对象并返回

make_pair("hehe", 123);	//构建出的匿名对象与上面的一致

make_pair 的定义如下所示:

template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
  return ( pair<T1,T2>(x,y) );
}

该函数实际会被编译器优化为 内联函数,因此不会造成过多消耗,可以放心使用

1.3、树型结构的关联式容器

所以在 C++ 标准中,共提供了四种 树型结构的关联式容器

  • set
  • multiset
  • map
  • multimap

关于 哈希结构的关联式容器 将在 哈希表 中学习

树型结构与哈希结构的关联式容器功能都是一模一样的,不过 哈希结构查找比树型结构快得多 -> O(1)

注:

  • STL 中选择的树型结构为 红黑树 RB-Tree
  • 树型结构中的元素 中序遍历 后有序,而哈希结构中的元素无序

2、set

2.1、什么是 set?

set 其实就是之前在 二叉搜索树key 的模型

C++【set 和 map 学习及使用】

set 只包含 实值 value,或者说它的 实值就是键值,键值就是实值

C++【set 和 map 学习及使用】

其中的 T 就是 set 的实值(键值),参数2 Compare 为存储依据,默认为升序,即符合 二叉搜索树 中序遍历的结果:升序,参数3 Alloc 是空间配置器,现在不必深究

作为 STL 中的容器,set 当然少不了迭代器,树型关联式容器迭代器的遍历结果为有序,所以迭代器遍历的本质是 中序遍历,同时 set 的迭代器还是一个 双向迭代器,支持 ++-- 操作

C++【set 和 map 学习及使用】
下面来看看 set 的相关操作

2.2、set 的使用

set 的构造函数如下图所示:

C++【set 和 map 学习及使用】
可以直接创建一个空 set 使用,也可以根据迭代器区间创建 set

注意:创建时需要指定实值的类型

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
	vector<int> arr = { 8,5,6,7,3,1,1,3 };

	set<int> s1;	//创建一个空的 set
	set<int> s2(arr.begin(), arr.end());	//创建包含数据的 set

	cout << "s1: ";
	for (auto e : s1)
		cout << e << " ";
	cout << endl;

	cout << "s2: ";
	for (auto e : s2)
		cout << e << " ";
	cout << endl;

	return 0;
}

C++【set 和 map 学习及使用】
就像 二叉搜索树 一样,set 是不支持数据冗余的,如果出现冗余的数据插入时,会失败,如果想存储冗余的数据,可以使用 multiset

set 中的常用功能

功能 用途
迭代器 遍历容器
empty 判断容器是否为空
size 当前容器中的元素数
max_size 容器的最大容量
insert 元素插入,根据特定条件插入至合适位置
erase 删除指定元素
swap 交换两个容器
clear 清空容器中的所有元素
find 查找实值是否存在并返回迭代器位置
count 统计容器中指定键值的数量

下面这段代码演示了上述功能的实际效果

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
	vector<int> arr = { 7,3,6,9,3,1,6,2 };
	set<int> s1(arr.begin(), arr.end());

	//迭代器遍历
	cout << "迭代器遍历结果: ";
	set<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//判空、求大小
	cout << "===================" << endl;
	cout << "empty(): " << s1.empty() << endl;
	cout << "size(): " << s1.size() << endl;
	cout << "max_size(): " << s1.max_size() << endl;

	//插入元素
	cout << "===================" << endl;
	cout << "insert(5): ";
	s1.insert(5);
	for (auto e : s1) cout << e << " ";
	cout << endl;

	//删除元素
	cout << "===================" << endl;
	cout << "erase(6): ";
	s1.erase(6);
	for (auto e : s1) cout << e << " ";
	cout << endl;

	//交换、查找、清理
	cout << "===================" << endl;
	set<int> s2(arr.begin() + 5, arr.end());
	s1.swap(s2);
	cout << "s1: ";
	for (auto e : s1) cout << e << " ";
	cout << endl;

	cout << "s2: ";
	for (auto e : s2) cout << e << " ";
	cout << endl;

	cout << "s1.find(9): ";
	cout << (s1.find(9) != s1.end()) << endl;

	cout << "s2.clear(): " << endl;
	s2.clear();

	cout << "s1: ";
	for (auto e : s1) cout << e << " ";
	cout << endl;

	cout << "s2: ";
	for (auto e : s2) cout << e << " ";
	cout << endl;

	return 0;
}

C++【set 和 map 学习及使用】

至于 count 也可以用来查找元素是否存在,对于 set 来说,键值 key 就是 实值 value,并且因为不允许冗余,所以只有一个 键值count 统计 键值 数量不就相当于 查找 吗?

#include <iostream>
#include <vector>
#include <set>
using namespace std;


int main()
{
	vector<int> arr = { 7,3,6,9,3,1,6,2 };
	set<int> s1(arr.begin(), arr.end());

	for (int i = 0; i < 10; i++)
	{
		if (s1.count(i))
			cout << i << " 在 set 中" << endl;
		else
			cout << i << " 不在 set 中" << endl;
	}

	return 0;
}

C++【set 和 map 学习及使用】

可以通过改变 set 模板参数2的方式,改变其中的顺序为 降序

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
	vector<int> arr = { 7,3,6,9,3,1,6,2 };
	set<int, greater<int>> s1(arr.begin(), arr.end());

	for (auto e : s1)
		cout << e << " ";

	return 0;
}

C++【set 和 map 学习及使用】

注意:键值 key 是不允许改变的,如果改变了,会破坏二叉搜索树的原则,因此即使是 set 中的普通迭代器,本质上也是 const 迭代器,非常神奇

C++【set 和 map 学习及使用】

2.3、set 的特点

set 具有以下特点:

C++【set 和 map 学习及使用】

set 还有一个亲兄弟:multiset,它允许数据冗余,即数据插入一定是成功的

2.4、multiset

multisetset 的另一个版本,对于 multiset 来说,插入冗余数据时,并不会失败

C++【set 和 map 学习及使用】
除此之外,multisetset 的操作没什么区别,一模一样

这里就不再赘述,而是单独演示一下允许数据冗余的效果

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
	vector<int> arr = { 3,5,3,4,5,9,2,3 };
	multiset<int> ms1(arr.begin(), arr.end());

	for (auto e : ms1)
		cout << e << " ";
	cout << endl;

	return 0;
}

C++【set 和 map 学习及使用】

值得一提的是,当在 multiset 中查找冗余的数据时,返回的是 中序遍历中,第一次出现的元素

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
	vector<int> arr = { 3,5,3,4,5,9,2,3 };
	multiset<int> ms1(arr.begin(), arr.end());

	auto it = ms1.begin();
	while (it != ms1.end())
	{
		cout << *it << " | " << &*(it) << endl;
		++it;
	}

	cout << "================" << endl;

	cout << "ms1.find(3): " << &*(ms1.find(3)) << endl;

	return 0;
}

C++【set 和 map 学习及使用】

所以,multiset 才是真正的排序,set 则是去重 + 排序

统计 键值countmultiset 中可以发挥真正效果

#include <iostream>
#include <vector>
#include <set>
using namespace std;


int main()
{
	vector<int> arr = { 3,5,3,4,5,9,2,3 };
	multiset<int> ms1(arr.begin(), arr.end());

	for (int i = 0; i < 10; i++)
		cout << i << "在 multiset 中的数量: " << ms1.count(i) << endl;

	return 0;
}

C++【set 和 map 学习及使用】

在实际中,multiset 用的比较少,重点掌握 set 即可


3、map

3.1、什么是 map?

map二叉搜索树 改造后的 key / value 模型,是一个真正意义上的 键值对,应用场景如下:

C++【set 和 map 学习及使用】
map 的定义如下

C++【set 和 map 学习及使用】
其中包含两个模板参数:

  1. Key 就是键值对中的 键值
  2. T 则是键值对中的 实值

map 中会用到前面提到过的 pair 结构,其中 first 表示键值,second 表示实值

map 也有迭代器,也是 双向迭代器

C++【set 和 map 学习及使用】

3.2、map 的使用

构造 map 有以下几种方法

C++【set 和 map 学习及使用】

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int main()
{
	vector<pair<string, int>> arr = { make_pair("G", 71), make_pair("A", 65), make_pair("F", 70) };

	map<string, int> m1;	//创建一个空的 map
	map<string, int> m2(arr.begin(), arr.end());	//创建包含数据的 map

	cout << "m1: " << endl;
	for (auto e : m1)
		cout << e.first << " | " << e.second << endl;
	cout << "========================" << endl;
	cout << "m2: " << endl;
	for (auto e : m2)
		cout << e.first << " | " << e.second << endl;

	return 0;
}

C++【set 和 map 学习及使用】

注意:在访问 map 中的 键值 和 实值 时,需要通过 pair 对象指定访问,比如 e.first

map 中的常用功能

功能 用途
迭代器 遍历容器
empty 判断容器是否为空
size 当前容器中的元素数
max_size 容器的最大容量
operator[] 按照键值,访问实值,如果没有,则新插入
insert 元素插入,根据特定条件插入至合适位置
erase 删除指定元素
swap 交换两个容器
clear 清空容器中的所有元素
find 查找实值是否存在并返回迭代器位置
count 统计容器中指定键值的数量

除了新增了一个 operator[] 以及部分函数返回值不一样外,与 set 没啥区别

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main()
{
	vector<pair<string, int>> arr{make_pair("z", 122), make_pair("a", 97),  make_pair("K", 75), make_pair("h", 104), make_pair("B", 66)};

	map<string, int> m1(arr.begin(), arr.end());

	//迭代器遍历
	cout << "迭代器遍历结果: ";
	map<string, int>::iterator it = m1.begin();
	while (it != m1.end())
	{
		cout << "<" << it->first << ":" << it->second << "> ";
		++it;
	}
	cout << endl;

	//判空、求大小、解引用
	cout << "===================" << endl;
	cout << "empty(): " << m1.empty() << endl;
	cout << "size(): " << m1.size() << endl;
	cout << "max_size(): " << m1.max_size() << endl;
	cout << "m1[""a""]: " << m1["a"] << endl;

	//插入元素
	cout << "===================" << endl;
	cout << "insert(""a"", 5): ";
	m1.insert(make_pair("a", 5));
	for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";
	cout << endl;

	//删除元素
	cout << "===================" << endl;
	cout << "erase(""a""): ";
	m1.erase("a");
	for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";
	cout << endl;

	//交换、查找、清理
	cout << "===================" << endl;
	map<string, int> m2(arr.begin() + 2, arr.end());
	m1.swap(m2);
	cout << "m1.swap(m2)" << endl;
	cout << "m1: ";
	for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";
	cout << endl;

	cout << "m2: ";
	for (auto e : m2) cout << "<" << e.first << ":" << e.second << "> ";
	cout << endl;

	cout << "m1.find(""B""): ";
	cout << (m1.find("B") != m1.end()) << endl;

	cout << "m2.clear()" << endl;
	m2.clear();

	cout << "m1: ";
	for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";
	cout << endl;

	cout << "m2: " << endl;
	for (auto e : m2) cout << "<" << e.first << ":" << e.second << "> ";
	cout << endl;

	return 0;
}

C++【set 和 map 学习及使用】
同样的,map 不允许数据冗余,如果想插入重复的数据,可以使用 multimap

map 插入的返回值比 set 略微复杂,因为 既要表示是否成功,也要返回插入成功的迭代器,所以返回值是一个 pair

C++【set 和 map 学习及使用】

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main()
{
	map<string, int> m1;
	auto ret = m1.insert(make_pair("a", 97));
	cout << "<" << ret.first->first << ":" << ret.first->second << ">" << " | " << ret.second << endl;

	ret = m1.insert(make_pair("a", 100));
	cout << "<" << ret.first->first << ":" << ret.first->second << ">" << " | " << ret.second << endl;

	return 0;
}

C++【set 和 map 学习及使用】

至于 findcountset 中的一样,可以用来判断元素是否存在,不过 find 返回的是 迭代器count 返回的则是 键值数

map 是支持修改 实值 value 的,因此 可以根据普通迭代器修改 实值

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main()
{
	map<string, int> m1;
	m1.insert(make_pair("a", 97));
	auto it = m1.find("a");
	cout << "<" << it->first << ":" << it->second << ">" << endl;

	it->second = 668;
	cout << "<" << it->first << ":" << it->second << ">" << endl;

	return 0;
}

C++【set 和 map 学习及使用】

使用 map 来实现水果统计的代码

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main()
{
	vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };
	map<string, int> table;

	for (auto& e : word)
	{
		auto ret = table.find(e);
		if (ret == table.end())
			table.insert(make_pair(e, 1));
		else
			ret->second++;
	}

	for (auto e : table)
		cout << "<" << e.first << ":" << e.second << ">" << endl;

	return 0;
}

C++【set 和 map 学习及使用】

可以实现统计,但这种写法太麻烦了,实际不会这么写,可以使用 operator[] 实现更高级的写法

3.3、map 中的 operator[]

operator[] 返回的是当前 键值 对应的 实值,如果当前 键值 不存在,则会插入新的 键值对

借助此特性,可把代码优化为

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main()
{
	vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };
	map<string, int> table;

	for (auto& e : word)
		table[e]++;

	for (auto e : table)
		cout << "<" << e.first << ":" << e.second << ">" << endl;

	return 0;
}

C++【set 和 map 学习及使用】

显然,map 中的 operator[] 是一个非常强大的功能

C++【set 和 map 学习及使用】

operator[] 的返回值为 mapped_type,即 实值 value 的引用,参数 key_type键值 key

重点在于 operator[] 的实现:如何凭借 键值 返回对应的 实值,并且做到新键值对的插入

(*((this->insert(make_pair(k, mapped_type()))).first)).second

C++【set 和 map 学习及使用】

总的来说,operator[] 返回时需要经历以下步骤:

  • 插入一个新的键值对 this->insert( make_pair(k, mapped_type()) )
  • 获取 insert 返回值中的 键值 返回值.first 即迭代器 iterator
  • 最后通过迭代器获取 实值 (*iterator).second

只需三步,即可获取 实值

其实上面那样定义还复杂了,可以优化为下面这个样子

( (this->insert( make_pair(k, mapped_type()) )).first )->second

所以一个 operator[] 兼顾了这几种功能:插入、修改、插入+修改、查找

map 中最强大的功能

3.4、map 的特点

归纳总结后,map 的特点如下图所示

C++【set 和 map 学习及使用】

注意:无论是查找、插入、删除还是排序,都只看 键值 key,至于 实值 value 的内容是什么,无所谓,它只不过是 键值 额外携带的一个信息包而已

multimap 允许出现键值冗余

3.5、multimap

C++【set 和 map 学习及使用】

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

所以 multimap 中没有提供 operator[]

C++【set 和 map 学习及使用】

C++【set 和 map 学习及使用】

除了 允许键值冗余没有 operator[] 这个两个特点外,multimapmap 在操作上没有区别

当然,查找 find 时,返回的是中序遍历中第一次出现元素的迭代器;计数 count 返回的则是当前 键值 的数量

multiset 一样,multimap 用的也比较少,重点掌握 setmap 即可


4、相关试题实战

学会使用 setmap 后,可以将其用于实战,比如在下面这两个题中,这两个容器可以让我们事半功倍

4.1、前K个高频单词

题目链接:692. 前K个高频单词

C++【set 和 map 学习及使用】

题目分析:题目很短,就是在一个字符串数组中,找出前 k 个出现频率最高的单词

注意:如果出现次数相同,则按字典序排序

这道题有很多种解法

解法一:map + 快排

利用 map 建立 <string, int> 的映射关系,在按照字典序排序的同时统计出每个单词的出现频率,再通过快排依照数量进行二次排序,选择前 k 个高频单词即可

因为基础版快排 不稳定,可能会导致频率相同的单词顺序出问题,即违背题目要求:如果出现频率相同,则按字典序排序

所以这里需要使用 稳定版快排 stable_sort,如果频率相同,保持原有顺序

//map + stable_sort
class Solution {
public:
    struct Compare
    {
        bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const
        {
            return kv1.second > kv2.second;
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {
        //统计每个单词出现的频率,同时先按照字典序排序
        map<string, int> table;
        for(auto e : words)
            table[e]++;

        //将处理好的数据存入数组中
        vector<pair<string, int>> vTable(table.begin(), table.end());

        //按照出现频率进行二次排序
        stable_sort(vTable.begin(), vTable.end(), Compare());

        //取出前 k 个高频单词
        vector<string> vs;
        for(int i = 0; i < k; i++)
            vs.push_back(vTable[i].first);
        
        return vs;
    }
};

C++【set 和 map 学习及使用】

注意:此时使用快排进行排序时,单个元素是 pair,需要自己写出仿函数进行排序,仿函数十分强大

难道基础版快排无法完成任务吗?
当然可以,只需要将 仿函数进行设计即可:优先按照出现频率排序,如果频率相同,则按照字典序排序即可

具体代码如下(用了一点 C++11 中的知识)

//map + sort
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        //统计每个单词出现的频率,同时先按照字典序排序
        map<string, int> table;
        for(auto e : words)
            table[e]++;

        //将处理好的数据存入数组中
        vector<pair<string, int>> vTable(table.begin(), table.end());

        //按照出现频率进行二次排序
        sort(vTable.begin(), vTable.end(), 
        [](const pair<string, int>& kv1, const pair<string, int>& kv2)->bool
        {
            return kv1.second == kv2.second ? kv1.first < kv2.first : kv1.second > kv2.second;
        });

        //取出前 k 个高频单词
        vector<string> vs;
        for(int i = 0; i < k; i++)
            vs.push_back(vTable[i].first);
        
        return vs;
    }
};

C++【set 和 map 学习及使用】

C++11 中的 lambda 表达式还是很香的

注意:优先按照出现频率进行排序,如果频率相同时,就按字典序排序,所以写成 kv1.first < kv2.first (小的单词排在前面,就是字典序)

解法二:map + set

同样的,先使用 map 统计单词出现频率,此时已经按照字典序进行了排序,然后将 pair 看作一个 键值 存入 set 中,改变 set 中的比较逻辑(先按出现频率排序,如果相关就按照字典序排序

整体思路与 map + sort 没啥区别,不过此时是直接使用 set 进行排序,没必要借助 vector

//map + set
class Solution {
public:
    struct Compare
    {
        bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const
        {
            return kv1.second == kv2.second ?
                   kv1.first < kv2.first :  //如果两个频率相等,比较字典序
                   kv1.second > kv2.second ;    //不相等比较频率
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string, int> table;
        for(auto e : words)
            table[e]++;
        
        set<pair<string, int>, Compare> sortSet(table.begin(), table.end());

        vector<string> vs;
        auto it = sortSet.begin(); 
        for(int i = 0; i < k; ++it, ++i)
            vs.push_back(it->first);

        return vs;
    }
};

C++【set 和 map 学习及使用】

解法三:map + multimap

这个解法就有点狠了,直接使用 mapmultimap 互导,完成排序

map 按照字典序排序,并统计出频率
multimap map 的基础上,按照 频率 排序

注意:需要使用 multimap,避免相同频率的单词丢失

//map + multimap
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        //先统计出现频率,同时按照字典序排序
        map<string, int> mapTbale;
        for(auto e : words)
            mapTbale[e]++;
        
        //将 map 中的数据存入 multimap 中,按照频率排序
        multimap<int, string, greater<int>> multimapTable;
        for(auto &e : mapTbale)
            multimapTable.insert(make_pair(e.second, e.first));
        
        //取出前k个高频单词
        vector<string> vs;
        auto it = multimapTable.begin();
        for(int i = 0; i < k; ++it, ++i)
            vs.push_back(it->second);
        
        return vs;
    }
};

C++【set 和 map 学习及使用】

这种写法十分巧妙,代码也很简洁,完美体现了 mapmultimap 的价值

关于这道题还有其他解法,比如 利用优先级队列解决 Tok-K,感兴趣的同学可以自己下去研究,这里就不再展开叙述

4.2、复杂链表的复制

题目链接:剑指 Offer 35. 复杂链表的复制

C++【set 和 map 学习及使用】

题目分析:复杂链表的深度拷贝,将题目给定的链表进行复制,这个链表比较特殊,不仅指向下一个节点,还随机指向空或其他节点

之前的解法是在两个节点新增节点,然后更改链接关系,比较麻烦,现在可以借助 map 建立映射关系,直接照着原链表更改链接关系即可

C++【set 和 map 学习及使用】

//剑指 Offer 35. 复杂链表的复制
//https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        map<Node*, Node*> copyNodeMap;  //存放原来的链表节点,及新的链表节点
        Node* cur = head;
        Node* copyHead = nullptr;
        Node* copyTail = nullptr;

        //先拷贝出链表
        while(cur)
        {
            Node* copy = new Node(cur->val);
            copyNodeMap[cur] = copy;

            if(copyHead == nullptr)
            {
                copyHead = copyTail = copy;
            }
            else
            {
                copyTail->next = copy;
                copyTail = copyTail->next;
            }

            cur = cur->next;
        }

        //初步拷贝已完成,进行随机指针的拷贝
        cur = head;
        while(cur)
        {
            //非常重要的一步
            copyNodeMap[cur]->random = copyNodeMap[cur->random];
            cur = cur->next;
        }

        return copyHead;
    }
};

C++【set 和 map 学习及使用】

map 在这种场景中是非常强大的!使得 原链表节点和新链表节点之间形成了一种羁绊关系,但 两者之间互不影响


5、补充:交集与差集

下面是一些补充知识,主要是关于 交集和差集

5.1、如何查找交集?

交集,指两个数组中相同的元素所构成的集合

求交集的步骤如下:

  1. 先将两个数组 排序 + 去重
  2. 遍历两个数组
  3. 如果不相等,小的 ++
  4. 相等就是交集,记录下来
  5. 其中一方走完,所有交集就查找完了

排序 + 去重,这就不就是 set 吗?

题目链接:349. 两个数组的交集

C++【set 和 map 学习及使用】

直接上代码

//349. 两个数组的交集
//https://leetcode.cn/problems/intersection-of-two-arrays/description/

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        //排序 + 去重
        set<int> s1(nums1.begin(), nums1.end());
        set<int> s2(nums2.begin(), nums2.end());

        //查找交集
        vector<int> v;
        auto it1 = s1.begin();
        auto it2 = s2.begin();

        while(it1 != s1.end() && it2 != s2.end())
        {
            if(*it1 < *it2)
                ++it1;
            else if(*it1 > *it2)
                ++it2;
            else
            {
                v.push_back(*it1);
                ++it1;
                ++it2;
            }
        }

        return v;
    }
};

C++【set 和 map 学习及使用】

5.2、如何查找差集?

至于差集的查找,思路和交集差不多

求差集的步骤如下:

  1. 先将两个数组 排序 + 去重
  2. 遍历两个数组
  3. 如果相等,同时 ++
  4. 不相等,小的一方记录后,再 ++
  5. 其中一方走完,再遍历另一方,此时其中的所有元素都是差集

🌆总结

以上就是本次关于 C++【set 和 map 学习和使用】的全部内容了,在这篇文章中我们先学习了 关联式容器相关知识,然后学习了 setmultisetmap 以及 multimap 的使用,最后通过一些题目见识到了 setmap 的强大之处,希望你在阅读本文后,能够收获相关知识


C++【set 和 map 学习及使用】

文章来源地址https://www.toymoban.com/news/detail-490958.html

相关文章推荐

C++ 进阶知识

C++【二叉搜索树】

C++【多态】

C++【继承】

STL 之 泛型思想

C++【模板进阶】

C++【模板初阶】

到了这里,关于C++【set 和 map 学习及使用】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++:map和set的介绍及使用

    目录 1. 关联式容器 2. 键值对 3. 树形结构的关联式容器 3.1 set 3.1.1 set的介绍 3.1.2 set的使用 3.2 map 3.2.1 map的介绍 3.2.2 map的使用 3.3 multiset 3.3.1 multiset的介绍 3.3.2 multiset的使用 3.4 multimap 3.4.1 multimap的介绍 3.4.2 multimap的使用 在之前文章中,我们已经接触过STL中的部分容器,比如:

    2024年02月03日
    浏览(36)
  • 【C++】详解map和set基本接口及使用

    关联式容器也是用来存储数据的,但与序列式容器不同的是,关联式容器里面存储的是 key, value 结构的键值对,因此**在数据检索时比序列式容器效率更高**。 键值对是用来表示 具有一一对应关系的一种结构 ,该结构中一般只包含两个成员变量 – key 和 value;其中 key 代表键

    2024年02月08日
    浏览(43)
  • 【C++】使用红黑树进行封装map和set

    🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘 。 🛸 C++专栏 : C++内功修炼基地 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真

    2024年02月07日
    浏览(46)
  • 【C++】map、set、multimap、multiset的介绍和使用

    我讨厌世俗,也耐得住孤独。 1. 之前所学的vector,list,deque等容器都是序列式容器,因为他们的底层数据结构都是线性的,并且数据结构中存储的都是元素数据本身,也就是单一的变量。 而下面所学的set、map、multimap、multiset等容器都是关联式容器,他们内部存储的不再是单

    2023年04月15日
    浏览(40)
  • 【C++】STL——set/multiset 和 map/multimap的使用

    在初阶阶段,我们已经接触过STL中的部分容器 比如:vector、list、deque、forward_list(C++11)等,这些容器统称为 序列式容器 ,因为其底层为线性序列的数据结构,里面存储的是元素本身。 而今天我们要学习的几个容器称为关联式容器,那什么是关联式容器?它与序列式容器有什

    2024年02月14日
    浏览(44)
  • 【C++】 使用红黑树模拟实现STL中的map与set

    前面的文章我们学习了红黑树,也提到了C++STL中的map和set的底层其实就是用的红黑树来实现的(而map和set的使用我们前面也学过了)。 既然红黑树我们也学习过了,那这篇文章我们就用红黑树来简单实现一下STL中的map和set,重点是学习它的框架。 上一篇文章我们实现了红黑

    2024年02月12日
    浏览(31)
  • 【C++】unordered_map和unordered_set的使用

    文章目录 前言 一、unordered_map的使用及性能测试 二、unordered_set的使用 1.习题练习 总结 unordered 系列关联式容器 : 在 C++98 中, STL 提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到O(logN) ,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,

    2024年02月06日
    浏览(48)
  • 【C++】unordered_set 和 unordered_map 使用 | 封装

    unordered_map官方文档 unordered_set 官方文档 set / map与unordered_set / unordered_map 使用功能基本相同,但是两者的底层结构不同 set/map底层是红黑树 unordered_map/unordered_set 底层是 哈希表 红黑树是一种搜索二叉树,搜索二叉树又称为排序二叉树,所以 迭代器遍历是有序的 而哈希表对应的

    2024年02月06日
    浏览(48)
  • C++:stl中set(multiset)和map(multimap)的介绍和使用

    本文主要从概念、常用接口和使用方法方面介绍set(multiset)和map(multimap)。 目录 一、概念介绍 1.关联式容器 2.键值对 3. 树形结构的关联式容器 二、set和multiset 1.set的介绍 2.set使用 1. set模板参数列表 2. set构造 3. set迭代器 4. set容量 5. set修改操作 6.set使用举例 3.multiset介绍 4.mul

    2024年02月08日
    浏览(40)
  • C++进阶--unordered_set、unordered_map的介绍和使用

      在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g 2 N log_2N l o g 2 ​ N ,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又

    2024年01月16日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包