在这里插入图片描述

3.调用列表初始化

int main()
{
    set<int> s3 = { 3,2,8,1,10,2 };
    for (auto e : s3)
    {
        cout << e << " ";
    }
    cout << endl;
    s3.erase(8);
    s3.erase(18);
    for (auto e : s3)
    {
        cout << e << " ";
    }
    cout << endl;

    auto pos = s3.find(10);
    if (pos != s3.end())
    {
        cout << *pos << endl;
        s3.erase(pos);
    }
    else
    {
        cout << "找不到" << endl;
    }
    for (auto e : s3)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">

3.2.2 set常见接口

在这里插入图片描述

#include 
void TestSet()
{
    // 用数组array中的元素构造set
    int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,
                   6, 8, 0 };
    set<int> s(array, array+sizeof(array)/sizeof(array));
    cout << s.size() << endl;
    // 正向打印set中的元素,从打印结果中可以看出:set可去重
    for (auto& e : s)
        cout << e << " ";
    cout << endl;
    // 使用迭代器逆向打印set中的元素
    for (auto it = s.rbegin(); it != s.rend(); ++it)
        cout << *it << " ";
    cout << endl;
    // set中值为3的元素出现了几次
    cout << s.count(3) << endl;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

3.3 set相关题目练习

我们需要知道,set是间接实现去重操作,如果存在该值就不再插入到set里面。

在这里插入图片描述

在这里插入图片描述

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());

        auto it1 = s1.begin();
        auto it2 = s2.begin();

        vector<int> v;
        while(it1 != s1.end() && it2 != s2.end())
        {
            if(*it1 > *it2) it2++;
            else if(*it1 < *it2) it1++;
            else if(*it1 == *it2)
            {
                v.push_back(*it1);
                it1++;it2++;
            }
        }
        return v;
    }
};这里拿出来的目的是体现了set的排序和去重的作用,在这种情况下可以帮助我们快速的做题。
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

五、multiset

5.1 multiset介绍

在这里插入图片描述

multiset当作可以set使用,同样属于key模型,区别在于muliset允许数据冗余也就是不去除重复数据。muliset是一种允许元素重复出现的集合类型,你可以像使用set一样使用它,但它会保留重复的元素并且不进行去重操作。

5.2 multiset使用

int main()
{
	multiset<int> s1;
	s1.insert(1);
	s1.insert(11);
	s1.insert(3);
	s1.insert(1);
	s1.insert(4);
	s1.insert(2);
	s1.insert(4);
	s1.insert(2);
	s1.insert(1);
	s1.insert(2);
	s1.insert(1);

	multiset<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		//*it = 1;
		cout << *it << " ";
		++it;
	}
	cout << endl;

	auto pos = s1.find(2);
	while (pos != s1.end() && *pos == 2)
	{
		cout << *pos << " ";
		++pos;
	}
	return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

在这里插入图片描述

六、map

6.1 map介绍

在这里插入图片描述

[map文档介绍](map - C++ Reference (cplusplus.com)):

map的模板参数说明

在这里插入图片描述

key与value通过成员类型value_type绑定在一起,为其取别名称为pair:typedef pair value_type;的意思就是说,之前key和value是单个单个的存储的,现在将key和value存在一个结构中。

在这里插入图片描述

6.2 掌握pair与make_pair

6.2.1 pair

在 C++ 中,pair 确实是一个模板类,允许用户将两个不同类型的数据组合在一起。模板的关键特性是它能够为不同类型的元素提供通用的实现,因此 pair 能够处理任意类型的两个数据项。

在这里插入图片描述

在这里插入图片描述

pair底层实现逻辑

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;

    //Key
	T1 first;
    //value
	T2 second;
	
	pair():first(T1(), T2()){}

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

};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

6.2.2 make_pair

在这里插入图片描述

make_pair 是 C++ 标准库中的一个函数模板,用于简化 std::pair 对象的创建。它可以自动推导出 pair 的类型,因此在构造 pair 对象时,无需显式地指定模板参数类型。

6.2.3 pair与make_pair区别

相对于pair使用中需要明确数据类型,而make_pair是函数模板可以自动去推类型

6.3 map对象多种插入场景

int main()
{
    map<string, string> dict;
    pair<string, string> kv1("sort", "排序"); dict.insert(kv1);

    //使用匿名对象
    dict.insert(pair<string, string>("left", "左边"));

    //使用make_pair,省略类型的书写
    dict.insert(make_pair("right", "右边"));

    //隐式类型转化
    pair<string, string> kv2("string", "字符串");
    dict.insert({ "string", "字符串" });

    //初始化列表
    map<string,string> dic2 = { {"string", "字符串"}, {"left", "左边"}, {"right", "右边"} };
    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

**关于以上两种map初始化的方式,推荐使用第一种写法。**同时需要注意的是dict.insert({ "string", "字符串" });这里不是列表初始化initializelist,而是调用pair构造函数,强制类型转化为pair类型插入数据。

6.3.1 map当中列表初始化

在这里插入图片描述

而对于map dic2 = { {"string", "字符串"}, {"left", "左边"}, {"right", "右边"} };属于初始化列表, std::map 的构造函数接受一个std::initializer_list> 类型的参数。所以当你使用 {"i", "dd"} 进行初始化时,它会被转换为 std::pair 类型,然后添加到 map 中。

6.4 将数据存在结构体

我们知道map中的元素类型是pair或make_pair,但是为什么map不分开来存储数据呢?而是需要将数据放在一个结构体中,通过结构体间接访问这些对象。

//map::iterator it = dict.begin();
//auto的作用就重复体现到了
auto it = dict.begin();
while (it != dict.end())
{
    // iterator key不能修改 value可以修改
    // const_iterator key不能修改 value不能修改
    //it->first += 'x';
    it->second += 'x';
    ++it;
}
cout << endl;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

关于key不是能被修改的。在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。对此key是不能随意修改的,可能会破坏搜索树结构,但key所对的内容value是支持修改的。

auto it = dict.begin();
while (it != dict.end())
{
    cout << (*it).first << ":" << (*it).second << endl;
}
cout << endl;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

对于如果不将key和value放在同一个结构体中,operator *只能返回一个值,但是我们希望可以得到key也可以得到value的值,对此将这两个变量放在结构体中,间接调用这两个变量,就会不出现冲突。对于上面的写法过于麻烦,这里喜欢使用→来解引用。

在这里插入图片描述

底层是调用operator ->接口,这里编译器会省略一个箭头进行优化,使得使用更加便捷。

6.5 map相关接口使用

6.5.1 erase

在这里插入图片描述

这里使用insert不会出现迭代器实现的问题,而erase可能会出现迭代器失效的问题,这里搜索树可以看成链表的加强版。

6.5.2 operaor[]

首先这里operator不是通过下标进行访问,而是通过key来进行访问对应value。关于operator[]实现不是通过find来实现而是通过insert来实现。

在这里插入图片描述

6.5.3 insert实现operator[]

在这里插入图片描述

在这里插入图片描述

解释上面两段话

对此我们可以看出来,insert在find功能基础上增加了插入的功能。这里insert的功能就是查找 + 插入(如果该元素存在,就是查找)

6.5.4 operaort[] 底层逻辑

V& operator[](const K& key)
{
    // 不管插入成功还是失败,pair中iterator始终指向key所在节点的iterator
    //这个V()就是调用默认构造,不是一个明确的数值
    pair<iterator, bool> ret = this->insert(make_pair(key, V()));
    iterator it = ret.fisrt;
    return it->second;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

八、map使用方面

8.1 operator[]使用

int main()
{
    map<string, string> dict;
    dict.insert({ "string", "字符串" });

    // 插入(一般不会这么用,单纯的插入)
    dict["right"];

    // 插入left + 修改 left对应内容
    dict["left"] = "左边";

    // "查找"
    cout << dict["string"] << endl;

    // 修改
    dict["right"] = "右边";

    string str;
    cin >> str;
    if (dict.count(str))
    {
        cout << "在" << endl;
    }
    else
    {
        cout << "不在" << endl;
    }
    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

8.2 如何统计水果出现的次数并排序(map用法和排序稳定性)

瑕疵版本

string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
                "苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };
map<string, int> countMap;
for (auto& e : arr)
{
    auto it = countMap.find(e);
    if (it != countMap.end())
    {
    	it->second++;
    }
    else
    {
    	//const pair& val = { e, 1 };
    	countMap.insert({ e, 1 });
    }
}

for (auto& kv : countMap)
{
    cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

优化版本

通过operator[]学习,我们可以对上面代码进行优化。

	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };
	map<string, int> countMap;
	for (auto& e : arr)
	{
		countMap[e]++;
	}

	for (auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在这里插入图片描述

通过次数来排序

只需将countMap的first和second位置数据,分别存放在sortMap的second和first位置上就行了

	map<int, string> sortMap;
	for (auto& kv : countMap)
	{
		//sortMap[kv.second] = kv.first;
		sortMap.insert({ kv.second, kv.first });
	}
	cout << endl;

	for (auto& kv : sortMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在这里插入图片描述

这里问题就是香蕉不见。对此我们知道map底层是通过搜索树实现的,而二叉搜索树是不允许冗余数据,并且是通过K对比较的,如果出现重复的K不会进行插入操作。(现在是次数对内容,而不是内容对次数,搞清楚K现在是谁)

multimap允许冗余

真的需要排序,可以使用multimap 允许冗余.

在这里插入图片描述

8.3 前K个高频单词

在这里插入图片描述

在这里插入图片描述

果是单纯排序话,可以符合按照次数进行排序,但是无法满足第二个需求,对此我们改写排序内部逻辑。

在这里插入图片描述

将内容插入map时完成了按照字典序排序的需求,但是接下来的快速排序属于不稳定排序,会破坏之前元素之间的相对位置,我们也可以将问题转换为使用一个稳定性好的排序,比如归并排序,map也提供了一个接口stable是稳定排序。

在这里插入图片描述

九、multimap

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

multimap使用事项:

  1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对,其中多个键值对之间的key是可以重复的
  2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,
  3. value_type是组合key和value的键值对:typedef pair value_type;
  4. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序的。
  5. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于key有序的序列。
  6. multimap在底层用二叉搜索树(红黑树)来实现。
  7. multimap中的key是可以重复的。
  8. multimap中的元素默认将key按照小于来比较
  9. multimap中没有重载operator[]操作,允许数据冗余,那么查找有冲突
  10. 使用时与map包含的头文件相同

在这里插入图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/2302_79177254/article/details/142032840","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的是店小二呀的文章"https://blog.csdn.net/2302_79177254/article/details/142032840"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!