C++学习之map/set

引入

好久没有更新了

关联式容器

在之前的学习中,我们已经接触到了STL中的部分容器,比如有:vector、list、deque、forward_list等,这些容器统称为序列式容器,因为其底层实现的原理为线性序列的数据结构,也就是存储元素的本身。
而关联式容器也是用来存储数据的,但是他又与序列式容器不同的是,其里面存储的是kv结构,也就是<key, value>的键值对,这种结构在数据检索时比序列式容器的效率更高。
而接下来的map、set、multimap、multiset是关联式容器中的树形结构,都是使用平衡搜索数(红黑树)作为其底层结构,容器中的元素是一个有序的序列。

键值对(pair)

首先先了解什么键值对。
键值对是用来表示具有一一对应的结构,<key, value>,包含着key和value两个成员变量,key代表键值,value表示与key对应的信息。比如:一个英汉互译的字典,那该字典中英文单词与其中文含义是一一对应的关系,也就可以通过该单词,在词典中就可以找到与其对应的中文含义。
而pair就是这样的结构
在这里插入图片描述

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的结构定义

Set

创建一个Set的对象

在这里插入图片描述
set的构造函数

// 我们先创建一个set的对象
// 方法一:
	set<int> s1;
// 方法二,创建时初始化
	set<int> s2 = { 1,2,3 };
// 方法三,同属数组初始化
	int a[] = { 1,2,1,5,8,5,3,2,6 };
	// 升序
	set<int> s3(a, a + sizeof(a) / sizeof(int));
	// 降序
	set<int,greater<int>> s4(a, a + sizeof(a) / sizeof(int));

而我们看到,set底层的平衡二叉树,我们有学过平衡二叉树将他中序遍历,他会呈现升序排列,但同时我们可以通过调整他的Compare,来将他变为降序,就比如上面的s4所展示的。

Set的基本功能

在这里插入图片描述在这里插入图片描述
Set的基本功能和之前学的list,queue,stack等都大部分有相似的地方,这里就不多说了。我们讲下一些特别的功能。

Set的删除方式的不同

当我们要删除Set中的一个元素时,可以使用set的erase功能和通过find找到其中的元素,再erase。但这有什么区别呢?

int a[] = { 1,2,1,5,8,5,3,2,6 };
set<int,greater<int>> s(a, a + sizeof(a) / sizeof(int));
for (auto e : s)
{
	cout << e << " ";
}
cout << endl;

set<int> ::iterator pos = s.find(1);
if (pos != s.end())
{
	s.erase(pos);
}

在这里插入图片描述
在这里插入图片描述

如果我们所要找的元素是set中的元素,则删除时没有什么区别,但当我们所要删除的元素不在set中,则erase删除不会改变set的结构,而我们通过find去找这个元素,再erase时,find在set中查找没有找到所要的元素,则他会指向set.end(),这样再erase时就会报错。

lower_bound()与upper_bound()

在这里插入图片描述
在这里插入图片描述

std::set<int> myset;
	std::set<int>::iterator itlow, itup;

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

	itlow = myset.lower_bound(30);                //       ^
	itup = myset.upper_bound(60);                 //                   ^

	myset.erase(itlow, itup);                     // 10 20 70 80 90

	std::cout << "myset contains:";
	for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)
		std::cout << ' ' << *it;
	std::cout << '\n';

在这里插入图片描述
在这段代码可以很明显的感受到30~60区间中的值都被删除了,
但要注意:
lower_bound():指的是大于或等于这个数的最小值(即 >= val)
upper_bound():指的是大于这个数的最小值 (即 > val)
这是要注意的问题。

multiset

在这里插入图片描述
set的底层的平衡二叉树,而平衡二叉树对其中的元素是去重的,但如果要获得元素可以相等的二叉树,则可以用multiset,其基本功能和set相同。

Map

在这里插入图片描述
其基本的结构与set非常相似,但要注意的是
在这里插入图片描述
这里map中所对应的value_type是pair类型,也就是上面所提到的。也就意味值,他是结构是<key, value>类型的结构

创建一个map对象

在这里插入图片描述

既然map是<key, value>的类型的结构。我们可以这样定义map的对象

	map<string, string> dict;
	/*pair<string, string> kv1("sort", "排序");
	dict.insert(kv1);*/

	// 匿名对象初始化
	dict.insert(pair<string, string>("sort", "排序"));
	dict.insert(pair<string, string>("test", "测试"));
	dict.insert(pair<string, string>("string", "字符串"));

	dict.insert(make_pair("left", "左边"));

其中map因为是pair的结果,其在insert中包含pair结构对象,但我们也可以使用make_pair()这一函数来实现,这样在insert的时候就不同考虑他的key和value的类型。

map的功能

map的功能与大部分stl容器相近
在这里插入图片描述

operator[]

我们主要讲下他的“[ ]”的功能,他不像我们的数组中的[]一样,因为数组中的[]所对应的空间是线性的,而map是平衡二叉树的结构,所以他不能用线性的思维去定义。

string arr[] = { "苹果","西瓜","苹果","西瓜",
	"苹果","苹果","西瓜","苹果","香蕉","苹果" ,"香蕉" };
	// 统计次数
	map<string, int> countMap;
	for (auto& str : arr)
	{
		map<string, int>::iterator it = countMap.find(str);
		if (it != countMap.end())
		{
			//(*it).second++;
			it->second++;

		}
		else
		{
			countMap.insert(make_pair(str, 1));
		}
	}

比如我们要统计次数,如果按通常的办法来写则像上面写的一样
在这里插入图片描述
但是如果我们用[ ]则会更方便

	string arr[] = { "苹果","西瓜","苹果","西瓜",
	"苹果","苹果","西瓜","苹果","香蕉","苹果" ,"香蕉" };
	map<string, int> countMap;
	for (auto& str : arr)
	{
		countMap[str]++;
	}
	map<string, int>::iterator it = countMap.begin();
	while (it != countMap.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;

其结果是一致的。
在这里插入图片描述
我们再看一段代码:

	map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	dict["insert"];
	dict["insert"] = "插入";
	dict["left"] = "左边";

在dict中,我们首先通过insert插入了(“sort”,“排序”)
在这里插入图片描述
但执行dict[“insert”]时,
在这里插入图片描述
我们注意到dict[“insert”]的second部分是“”
但继续执行代码:
在这里插入图片描述
单词“insert”对应的second变为了“插入”
继续执行:
在这里插入图片描述
单词"left“和“左边”一同插入进去了。
通过上面逐条指令监视执行可以看出,operator[]的功能包括插入和修改
在文档中有一句话是:
在这里插入图片描述
其相当于调用insert函数,但我们来分析insert函数
insert函数的返回值
通过其返回值的定义,我们可以间接退出:
1、当key在map中是,返回pair<key_iterator, false>
2、当key不在map中时,返回pari<new_key_iterator, true>
也就是:

V& operator[]( const K& key )
{
	pair<iterator, bool> ret = insert(make_pair(key,v()));
	return ret.first->second;
}

multimap

其原理和multiset一样,可以存储相同数据的多个值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值