【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?

目录

引子

1.set(key模型)

        1.2.set的使用

2.multisetset(支持键值冗余的set)

3.map(kv模型)

        3.2map的插入(将使用pair和make_pair)

        3.2.1pair和make_pair

        3.3 map常使用typedef来缩短代码,增加可读性

        3.4修改value

4.operator[]的底层和原理

        4.1insert的奇怪的返回值

        4.2operator[]是一定是随机访问吗?

        4.3统计一下孩子最喜欢的运动


引子

关联式容器和序列式容器

  • 序列式容器:vector、list、string(O(N))
  • 关联式容器:map和set(底层是平衡搜索二叉树)(O(logN))

优势:关联式容器相较于序列式容器在数据检索时比序列式容器效率更高

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和valuekey代表键值, value表示与key对应的信息

树形结构的关联式容器

  • 树型结构的关联式 容器主要有四种:mapsetmultimapmultiset

1.set(key模型)

  •  set是key的模型,只有一个key没有value

1.1. set的模板参数列表

【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第1张图片

1.2.set的使用

1.2.1.set的迭代器的使用

  1. 支持迭代器那么就支持范围for(C++11),有2种遍历
  2. 可以利用set的特性去重,set的插入已经有的元素(key)会失败
  3. 元素(key)不能给更改,因为更改将会打乱结构

	// 1、排序+去重
	// 遍历方式1:
	set::iterator it = s.begin();
	while (it != s.end())
	{
		// 不能修改已经插入的值
		// *it += 1;

		cout << *it << " ";
		++it;
	}
	cout << endl;

	set::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit << " ";
		++rit;
	}

	cout << endl;

	// 遍历方式2:
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

执行结果

 1.2.2.set的找(find)的使用

  1. 找某一个key,如果set内有找到返回指向key位置迭代器,set内没有找不到返回end()位置的迭代器,所有判读it!=end()
  2. 可以利用set的特性实现检查单词拼写是否正确,思路:词库中单词都放进set的对象中,把每个写出来的单词去set中查一下,在就是正确的,不在就是错误的拼写
    // 检查单词拼写是否正确
	// 思路:词库中单词都放进set的对象中,把每个写出来的单词去set中查一下,在不在,在就是正确的,不在就是错误的拼写
	set strSet;
	strSet.insert("sort");
	strSet.insert("left");
	strSet.insert("right");
	strSet.insert("insert");

	for (const auto& e : strSet)
	{
		cout << e << " ";
	}
	cout << endl;

	// ...
	set::iterator ret = strSet.find("sort");
	if (ret != strSet.end())
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "没有找到" << endl;
	}

	ret = strSet.find("map");
	if (ret != strSet.end())
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "没有找到" << endl;
	}

执行结果

 1.2.3.set的删除(erase)的使用

  1. erase可以是使用迭代器也可以使用直接key
  2. 要注意使用迭代器要判断有没有,没有删除会报错,key这不会有这个顾虑,没有也米有关系

没有判断且没有这个key,报错

	set s;
	s.insert(3);
	s.insert(1);
	s.insert(2);
	s.insert(14);
	s.insert(36);
	s.insert(4);
	s.insert(3);
	s.insert(3);

	// 先查找,找到了再删。没找到,也去删会报错
	auto pos = s.find(4);
	if (pos != s.end())
	{
		s.erase(pos);
	}

	pos = s.find(40);
	//s.erase(pos);
	if (pos != s.end())
	{
		s.erase(pos);
	}

	// 在就删除,不在就不处理也不报错
	s.erase(3);
	s.erase(30);

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

执行结果

2.multisetset(支持键值冗余的set)

2.1. multiset的模板参数列表

一模一样,multiset和set不同的是,multiset支持插入相同的key ,或者说支持键值冗余

【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第2张图片

2.2. multiset和set的使用上的一些不同

  • 大部分都相同
  1. 排序遍历出的结果,可能存在有相同的key
  2. 证明:find查找的val有多个的时候,那么他找到的是中序的第一个?中序遍历,最终打印出4个3就可证明
  3. count的高光,count找key在容器中有几个,如果是set不支持冗余,所以我称他为count的高光时刻
  4. 删除容器中key存在多份的,删一个则全部删除
	// multiset允许键值冗余,使用方法基本跟set一致
	// 就下面几个地方有一些差异
	multiset s;
	s.insert(3);
	s.insert(1);
	s.insert(2);
	s.insert(14);
	s.insert(36);
	s.insert(4);
	s.insert(3);
	s.insert(3);
	s.insert(3);

	// 排序
	multiset::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// find查找的val有多个的时候,那么他找到的是中序的第一个
	multiset::iterator pos = s.find(3);
	while (*pos == 3)
	{
		cout << *pos ;
		++pos;
	}
	cout << endl;

	//count的高光
	cout << s.count(3) << endl;
	cout << s.count(4) << endl;

	//删除容器中key存在多份的,删一个则,全部删除
	s.erase(3);
	it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

执行结果

  • 【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第3张图片

3.map(kv模型)

  •  kv模型:成员变量有两个key和value,就是键值对

作用:例如key为中文,value为英文翻译

3.1. multiset的模板参数列表

【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第4张图片

3.2map的插入(将使用pair和make_pair)

学习map的插入必须先学习pair这个类,为什么?

插入格式:m.insert(pair(1,1.1))或者m.insert(make_pair(2,2.2))

3.2.1pair和make_pair

pair这个类不需要我们写,库里有

  • m.insert(pair(1,1.1)),调用pair类的构造函数,在插入map中,为什么封装?

拿到key我们返回值拿不到value

template 
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)
	{}
};

make_pair,用函数调用构造会不会有效率上的下降?不会,这里被优化了

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

map的插入

  1. 调用pair的构造函数,构造一个匿名对象来插入,匿名对象的优势,不是要先创建一个对象,在插入
  2. 调用函数模板,构造对象,好处:不需要声明pair的模板参数,让编译器推演,写起来更方便
map m;
	//调用pair的构造函数,构造一个匿名对象来插入
	m.insert(pair(1, 1.2));
	m.insert(pair(3, 3.2));
	m.insert(pair(2, 2.2));

	//调用函数模板,构造对象
	//好处:不需要声明pair的模板参数,让编译器推演,写起来更方便
	m.insert(make_pair(4, 4.2));
	map::iterator it = m.begin();
	while (it != m.end())
	{
		//cout << *it << endl;
		//cout << (*it).first << ":" << (*it).second << endl;
		cout << it->first <<":"<< it->second << endl;
		++it;
	}

执行结果

3.3 map常使用typedef来缩短代码,增加可读性

	typedef std::map DICT;
	typedef std::pair DICT_KV;
	typedef std::map::iterator DICT_ITER;

	DICT dict;
	dict.insert(DICT_KV("insert", "插入"));
	dict.insert(std::make_pair("sort", "排序"));
	dict.insert(std::make_pair("left", "左边"));

	DICT_ITER dit = dict.begin();
	//auto dit = dict.begin();
	while (dit != dict.end())
	{
		cout << dit->first << ":" << dit->second << endl;
		++dit;
	}
	cout << endl;

执行结果

 3.4修改value

  1. pair的成员变量分别叫first(first)和second(value)
  2. 迭代器是容器的成员变量的指针,那么就是一个pair类的指针
  3. first(key)不可修改,second(value)可以修改
	typedef std::map DICT;
	typedef std::pair DICT_KV;
	typedef std::map::iterator DICT_ITER;

	DICT dict;
	dict.insert(DICT_KV("insert", "插入"));
	dict.insert(DICT_KV("sort", "排序"));
	dict.insert(DICT_KV("left", "左边"));

	DICT_ITER dit = dict.begin();
	while (dit != dict.end())
	{
		dit->second.insert(0, "{");
		dit->second += "}";

		++dit;
	}
	cout << endl;

	dit = dict.begin();
	while (dit != dict.end())
	{
		cout << dit->first << ":" << dit->second << endl;
		++dit;
	}
	cout << endl;

	// 修改map的value数据
	auto ret = dict.find("left");
	if (ret != dict.end())
	{
		//ret->second.insert(ret->second.size() - 1, "、剩余");
		// 可读性优化技巧
		string& str = ret->second;
		str.insert(str.size() - 1, "、剩余");
	}

	dit = dict.begin();
	while (dit != dict.end())
	{
		cout << dit->first << ":" << dit->second << endl;
		++dit;
	}
	cout << endl;

执行结果

  • 【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第5张图片

4.operator[]的底层和原理

4.1insert的奇怪的返回值

即然map是一个键值对为且不支持冗余的容器,那么是不是可以用来统计一组数据出现的次数呢?

	//统计
	string arr[] = { "足球","足球", "足球", "篮球", "足球", "足球","篮球","乒乓球","篮球","乒乓球","乒乓球", };

	map countMap;
    //核心算法
	for (const auto& str : arr)
	{
		//先插入,如果str已经在map中,insert会放回str所在节点的迭代器,再++;
		pair::iterator, bool> it = countMap.insert(make_pair(str,1));
		if (it.second ==false )
		{
			it.first->second++;
		}
	}
    //打印
	for (const auto& e : countMap)
	{
		cout << e.first << ":" << e.second << endl;
	}

执行结果

 那么问题来了这句代码是什么鬼pair<map::iterator, bool> it = countMap.insert(make_pair(str,1));

  • pair<map::iterator, bool> it这是一个对象,不是迭代器指针

以下资料都来自map::insert - C++ Reference (cplusplus.com)

查以下map的insert的返回值

  • insert的返回是一个pair对象,
    第一个参数为map的迭代器指针,第二个参数为bool值

插入成功和失败

  1. 插入成功返回的pair对象,第一个成员变量(first)是指向map的插入元素的迭代器指针,第二个成员变量(second)插入成功为true
  2. 插入失败返回的pair对象,插入失败说明已经有这个key了,第一个成员变量(first)是指向包含这个key的pair对象的迭代器指针,第二个成员变量(second)插入失败为false

【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第6张图片

4.2operator[]是一定是随机访问吗?

不是随机访问,operator[]的实现,简化了insert

	string arr[] = { "足球","足球", "足球", "篮球", "足球", "足球","篮球","乒乓球","篮球","乒乓球","乒乓球", };
	map countMap;

	for (const auto& str : arr)
	{
		countMap[str]++;
	}
    //打印
	for (const auto& e : countMap)
	{
		cout << e.first << ":" << e.second << endl;
	}

执行结果

 operator的源码解析

【c++set-map使用篇】1insert的奇怪的返回值 2operator[]是一定是随机访问吗?_第7张图片

operator[]的扩展用法

	map dict;
	dict.insert(make_pair("right", "右边"));
	dict["key"] = "关键";//插入+修改
	dict["insert"];//插入
	dict["insert"] = "插入";//修改
	dict["right"] = "右边,正确";//修改
	for (const auto& e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}

 执行结果

 4.3统计一下孩子最喜欢的运动

思路先把数据统计在一个map,再用另一个map把diyigemap的value当做我的key

	string arr[] = { "乒乓球","足球","足球", "足球", "篮球", "足球", "足球","篮球","乒乓球","篮球","乒乓球","乒乓球", };
	map countMap;
	for (const auto& str : arr)
	{
		countMap[str]++;
	}
	// 利用map排序  -- 拷贝pair数据
	//map sortMap;
	map> sortMap;
	for (auto e : countMap)
	{
		sortMap.insert(make_pair(e.second, e.first));
	}
	for (const auto& e : sortMap)
	{
		cout << e.first << ":" << e.second << endl;
	}

执行结构

你可能感兴趣的:(c++,c++,开发语言,1024程序员节)