关联式容器——map和set详解

troop主页
今日鸡汤:Never bend your head.Always hold it high.Look the world straight in the face.
加油!成为最好的编程大师

前言

我们前几篇文章讲了搜索二叉树,我们提到了搜索二叉树的应用就是K结构和KV结构,今天我们要提到的这两个关联式容器就是这两个结构的实现,废话少说直接今日我们的内容。

一,set

  1. set是按照一定次序存储元素的容器
  2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
    set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行
    排序。
  4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
    子集进行直接迭代。
  5. set在底层是用二叉搜索树(红黑树)实现的。
    最后一点也是我们后面学习的重点。

1.1set的使用

在这里插入图片描述
这里的Compare就是比较大小,默认是按照小于的比较。
set的使用上没有太多可以讲的,他的绝大多数容器我们都是可以类比之前学习过的。
我们直接写一些代码来熟悉它。

	//去重+排序
	set<int> s;
	s.insert(2);
	s.insert(7);
	s.insert(7);
	s.insert(1);
	s.insert(10);
	s.insert(9);
	s.insert(14);
	s.insert(17);

	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

在这里插入图片描述
set就是二叉搜索树的K结构,她最基本就是排序+去重操作。
在这里插入图片描述
值得注意的是set的find,它的返回值是一个迭代器,并且当找不到这个数的时候返回的是set的最后一个位置,那么我们的代码就可以这样写。

	set<int>::iterator pos1 = s.find(7);
	if (pos1 != s.end())
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "没有" << endl;
	}
	cout << endl;
	set<int>::iterator pos2 = s.find(100);
	if (pos2 != s.end())
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "没有" << endl;
	}
	cout << endl;

在这里插入图片描述
同时set还有一个新的函数count
在这里插入图片描述
与find类似它也是查找,但他会返回这个数出现的次数,但是我们知道set是去重操作的,这个数最多一个。
这个函数是给multiset使用的。

特殊的set(multiset)

这个multiset就可以称得上真正的排序了,因为它只排序不去重

	multiset<int> s;
	s.insert(2);
	s.insert(7);
	s.insert(7);
	s.insert(7);
	s.insert(7);
	s.insert(7);
	s.insert(1);
	s.insert(10);
	s.insert(9);
	s.insert(14);
	s.insert(17);
	for (auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << s.count(7) << endl;

在这里插入图片描述
这里的count就发挥了他的作用了。

二,map

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元
    素。
  2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
    内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
    value_type绑定在一起,为其取别名称为pair:
    typedef pair<const key, T> value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
    对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

2.2map的使用

在这里插入图片描述
在这里插入图片描述
map就是KV结构,C++这里的设计就是把KV一起放在一个结构体中去使用。
我们来看看map的插入。
在这里插入图片描述
注意看,insert的pair和上面的pair是两个不一样的pair,等会到[]我们会重点提到这个。
insert的插入方式有很多。

	map<string, string> dict;

	//匿名对象
	dict.insert(pair<string, string>("sort", "排序"));
	//有名对象
	pair<string, string> kv("string", "字符串");
	dict.insert(kv);
	//{}:c++11支持的隐式类型转换
	dict.insert({ "apple","苹果" });
	//make_pair:c++98
	dict.insert(make_pair("banana", "香蕉"));

我个人更喜欢make_pair这样的写法。

	map<string, string> dict;

	//匿名对象
	dict.insert(pair<string, string>("sort", "排序"));
	//有名对象
	pair<string, string> kv("string", "字符串");
	dict.insert(kv);
	//{}:c++11支持的隐式类型转换
	dict.insert({ "apple","苹果" });
	//make_pair:c++98
	dict.insert(make_pair("banana", "香蕉"));
	map<string, string>::iterator it = dict.begin();
	while (it != dict.end())
	{
		//cout << (*it).first << (*it).second << endl;
		//it->==pair*;pair->first   优化成一个->
		cout << it->first << it->second << endl;	
		++it;
	}

在这里插入图片描述
这里我们要说一说

cout << it->first << it->second << endl;
这里省略了一个->,他的完整操作是operator调用->,返回了数据的指针(pair*),然后pair*再调用->。这里优化成了一个箭头。

还有一点要注意:map里面看数据重不重复,只看key,只要key相同就构成冗余
同样的map也有一个支持冗余的就是:multimap

void map_test2()
{
	multimap<string, string> dict;
	dict.insert(pair<string, string>("sort", "排序"));
	dict.insert(pair<string, string>("sort", "12"));
	dict.insert(pair<string, string>("sort", "123"));
	dict.insert(pair<string, string>("sort", "1234"));

	for (auto& kv : dict)
	{
		cout << kv.first<<":" << kv.second << endl;
	}
}

在这里插入图片描述

2.3

在这里插入图片描述

在这里插入图片描述
上面的翻译大致是:调用[]相当于调用这一大串。
在这里插入图片描述
==intsert中的pair的first指向了被插入元素所在位置的迭代器。

	string arr[] = { "香蕉","橘子","梨","苹果","草莓","香蕉","橘子", "香蕉","橘子", "香蕉","橘子", "香蕉","橘子",
					"西瓜","榴莲" };
	map<string, int> countMap;


	for (auto& e : arr)
	{
		pair<map<string, int>::iterator, bool> ret;
		ret = countMap.insert(make_pair(e, 1));
		//如果不存在那就直接插入,如果存在那这个inset什么事情都不会干,此时这里的bool值就是false
		if (ret.second == false)
		{
			//ret.first==iterator(被插入元素所在位置的迭代器),这个元素的second就是记录个数的
			ret.first->second++;
		}
	}

ret.first==iterator(被插入元素所在位置的迭代器);
理解了[]的原理,下标还是非常的好用的。上面这个统计次数的代码,用下标只要一行。

	for (auto& e : arr)
	{
		countMap[e]++;
	}

总结

总的来说map和set就是我们已经学过的知识,知识换了包装而已,这里的重点就是要理解这个下标的原理,多通过代去深化自己的知识,下一节我们就要开始讲
AVL树和红黑树了。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-09 17:42:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-09 17:42:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-09 17:42:04       82 阅读
  4. Python语言-面向对象

    2024-04-09 17:42:04       91 阅读

热门阅读

  1. Vue.nextTick() 使用场景及实现原理

    2024-04-09 17:42:04       36 阅读
  2. DockerFile定制镜像

    2024-04-09 17:42:04       32 阅读
  3. Vue3 · 小白学习全局 API:常规

    2024-04-09 17:42:04       36 阅读
  4. 面试前必看,仅供参考

    2024-04-09 17:42:04       33 阅读
  5. 蓝桥杯算法题:蓝桥公园

    2024-04-09 17:42:04       38 阅读
  6. 图神经网络学习记录——图信号处理常见方法

    2024-04-09 17:42:04       35 阅读
  7. python pytest 面试题

    2024-04-09 17:42:04       38 阅读
  8. spring获取bean

    2024-04-09 17:42:04       31 阅读
  9. # 计算机视觉入门

    2024-04-09 17:42:04       34 阅读
  10. 算法刷题记录 Day41

    2024-04-09 17:42:04       39 阅读