C++学习进阶:set 和 map

目录

set

什么是set

set的使用

insert

erase

lower_bound和upper_bound 

count 

equal_range 

 multiset的使用

实现相同数据的插入

删除掉符合的所有数据

查找并返回第一个符合的数据

 计数某个数据个数

​编辑

取出相同数据的前后迭代器

map

map的插入与遍历

 map的[]重载

[]重载的原理

[]体现的功能 

map与multimap


set

什么是set

C++中的set是一种基于红黑树实现的关联容器,用于存储唯一且已排序的元素集合。set中的元素按照一定的顺序排列,插入和删除操作的时间复杂度为O(log n),查找操作的时间复杂度也为O(log n)。set中的元素必须是可比较的,因此需要重载运算符或者提供自定义比较函数。

如图set分为普通set以及multiset,普通set就是上面所说存储唯一且已排序的元素结合,而multiset就是可以存储多个相同key的set

那么我们先开始进行set的学习

set的使用

跟以往学过的STL容器一致,set的使用如出一辙,所以相同的函数接口就不做过多的赘述,主要讲一下用法上的区别,和没见过的几个接口函数的使用!

insert

首先,测试一下insert函数接口

void test_set1()
{
	// 查找在不在
	// 排序 + 去重
	set<int> s;
	s.insert(5);
	s.insert(2);
	s.insert(6);
	s.insert(1);
	s.insert(1);
	s.insert(1);
	s.insert(2);


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

}

 如图最终通过迭代器打印发现实现了1的去重,以及排序!

所以在一个场景中,如果一段数据需要去重以及排序,我们可以通过将这段数据借助set输入,然后实现去重和排序 

另外set的key值不可修改! 体现在解引用时不能赋值

erase

在insert部分的代码上加入即可测试erase

	// 删除存在的
	s.erase(2);

	// 删除不存在的
	s.erase(3);

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

不会导致报错,表示删除set中不存在的元素,不会产生异常 

另外我们在阅读erase这个函数接口的文档时 ,观察到它支持迭代器区间删除,那么我们就可借助find来定位,然后通过迭代器删除

void test_set1()
{
	set<int> s;
	s.insert(7);
	s.insert(9);
	s.insert(6);
	s.insert(5);
	s.insert(3);
	s.insert(1);

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

	set<int>::iterator it1 = s.find(3);
	auto it2 = s.find(7);

	s.erase(it1, it2);

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

由此可得删除的区间符合左闭右开原则

lower_bound和upper_bound 

set中的lower_bound和upper_bound是两个函数,它们都用于在有序集合中查找元素。其中,lower_bound函数返回一个迭代器,指向第一个不小于给定值的元素,而upper_bound函数返回一个迭代器,指向第一个大于给定值的元素。如果给定值在集合中不存在,那么lower_bound和upper_bound函数都返回一个指向第一个大于给定值的元素的迭代器.

void test_set3()
{
	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);              //
	itLow = myset.lower_bound(25);                // >= val值位置的iterator
	itUp = myset.upper_bound(70);                 // >  val值位置的iterator

	// [25, 70]
	myset.erase(itLow, itUp);                     // 10 20 70 80 90

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

对于lower和upper的使用中,itLow取到了30所在位置的迭代器地址,itUp取到了80位置的迭代器,再通过左闭右开原则,删除时删掉了30 40 50 60 70,对于范围删除我们也可以借助upper和lower来确定迭代器大致范围,用find的话就需要知道这个数据是否存在于set中。

count 

set中的count()函数用于统计某个元素在set容器中出现的次数。如果该元素存在,则返回1,否则返回0。这个对于set来说有点鸡肋,count主要是在multiset发挥作用

equal_range 

在cpp文档中我们知道了它的返回值是pair,也就是键值对。并且内部存放了lower_bound和upper_bound

(pair是C++ STL中的一个模板类,用于将两个不同类型的值组合在一起。其中,第一个值的类型被称为first,第二个值的类型被称为second)

// 测试equal_range
void test_set8()
{
	set<int> myset;
	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


	pair<set<int>::iterator, set<int>::iterator> p1 = myset.equal_range(30);

	cout << "the lower bound points to: " << *p1.first << endl;
	cout << "the upper bound points to: " << *p1.second << endl;

	pair<set<int>::iterator, set<int>::iterator> p2 = myset.equal_range(25);

	cout << "the lower bound points to: " << *p2.first << endl;
	cout << "the upper bound points to: " << *p2.second << endl;


}

到了这里set的初步使用的讲解也就结束了,接下来我们讲一下multiset吧

 multiset的使用

实现相同数据的插入
void test_set5()
{
	multiset<int> ms;
	ms.insert(5);
	ms.insert(2);
	ms.insert(6);
	ms.insert(1);
	ms.insert(1);
	ms.insert(1);
	ms.insert(2);

	set<int>::iterator it = ms.begin();
	while (it != ms.end()) {

		// *it = 10;
		cout << *it << " ";
		++it;
	}
	cout << endl;


}

删除掉符合的所有数据

在insert的基础上进行删除1的key

    // 记录删除个数	
    size_t n = ms.erase(1);
	cout << "n: " << n << endl;
	for (auto e : ms)
	{
		cout << e << " ";
	}
	cout << endl;

我们可以用n来记录erase的数据个数 

查找并返回第一个符合的数据
void test_set6()
{
	multiset<int> ms;
	ms.insert(5);
	ms.insert(2);
	ms.insert(6);
	ms.insert(1);
	ms.insert(1);
	ms.insert(1);
	ms.insert(2);

	set<int>::iterator it = ms.find(2);
	while (it != ms.end()) {

		// *it = 10;
		cout << *it << " ";
		++it;
	}
	cout << endl;

}

 计数某个数据个数
// 测试multiset下的count
void test_set7() {

	multiset<int> ms = { 1,1, 2, 3, 3, 3, 3, 4, 5 };
	
	cout << ms.count(1) << endl; // 输出:2
	cout << ms.count(3) << endl; // 输出:4
	cout << ms.count(6) << endl; // 输出:0

}

显而易见,count在multiset中有特别大的作用,我们可以将文本插入multise<string>然后读取,某一个单词,来记录出现的次数 

取出相同数据的前后迭代器
// 测试multiset下的equal_range
void test_set9() {

	multiset<int> ms;
	multiset<int>::iterator itLow, itUp;

	for (int i = 1; i < 7; i++) ms.insert(i * 10);
	ms.insert(30);
	ms.insert(30);
	ms.insert(30);
	for (auto e : ms)
		cout << e << " ";
	cout << endl;

	pair<multiset<int>::iterator, multiset<int>::iterator> p1 = ms.equal_range(30);

	itLow = p1.first;
	itUp = p1.second;
	
	while (itLow != itUp) {

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

}

map

在map中我们就主要讲一下普通的map,multimap仅仅也是支持相同名称的key,并没有什么特殊的用法!不过multimap不支持 [] 重载

map的插入与遍历

// map的插入和遍历
void test_map1() 
{

	map<string, string> dict;
	// 匿名对象构造pair对象
	dict.insert(pair<string, string>("语文", "Chinese"));
	dict.insert(pair<const char*, const char*>("数学", "Math"));
	
	// 通过函数模版 make_pair 自适应类型
	dict.insert(make_pair("英语", "English"));

	map<string, string>::iterator it = dict.begin();

	while (it != dict.end())
	{
		// 迭代的it下存放的是 pair对象
		cout << (*it).first << ":" << (*it).second << endl;
		++it;
	}
	for (auto& e : dict)
	{
		// e.first = 'x';	first不可修改
		// e.second = 'x';	second支持修改
		cout << e.first << ":" << e.second << endl;
	}
}

在说map的insert()之前,我们需要了解pair和pair与insert的关系

 map的[]重载

[]重载的原理
// 测试map中的[]
void test_map2() {
	string str[] = { "嗯","哦","噢","喔","好","噢","噢","好" };
	map<string, int> countMap;
	for (auto& s : str) {

		// 常规实现计数要求
		//map<string, int>::iterator ret = countMap.find(s);
		// 
		// 找不到		
		//if (ret == countMap.end())
		// 	countMap.insert(make_pair(s, 1));
		//else
		//	(*ret).second++;


		// map中实现计数要求
		// 借助[]重载实现
		countMap[s]++;	
		// 分成多样的功能
	}

	for (auto& s : countMap) {

		cout << s.first << ":" << s.second << endl;
	}
}

我们在搜索二叉树的学习中,实现过类似一段话的单词出现个数的记录,核心代码

但是map中重载了[]来便于实现这一块的内容,以及更深层次的东西,现在我们开始研究一下map中的operator[]

 从这个图我们知道map的[]重载设计较复杂。

1.关于insert的返回值pair<iterator, bool>以及传入值pair<key, value_type>类型,虽然都是pair但是pair内存储变量类型不同

2.借助insert返回的这个pair<iterator, bool>的first找到迭代器位置,在通过迭代器解引用来找到存放的map<string, int>的某一个节点存放的pair<first, second>,最终通过这个pair对second进行操作。其中first为key,second为value,对应下图为好 与 2

讲到这里[]重载的原理就差不多了

[]体现的功能 
// 测试[]的功能
void test_map3() {
	map<string, string> dict;

	dict["语文"];	// 插入
	dict["语文"] = "Chinese";		// 修改 
	cout << dict["语文"] << endl;	// 访问

	dict["数学"] = "Math";	// 插入 + 修改

	for (auto& s : dict) {

		cout << s.first << ":" << s.second << endl;
	}
}

这一部分就看代码注释就好!

map与multimap

几乎一致只是multimap不支持[]重载,这个我们也能理解,因为multimap可以实现多个相同的key,那么就会导致[]查找的second,不知道查找哪一个!

multimap应用场景

// 测试multimap
void test_map4() {

	multimap<string, string> ms;
	ms.insert(make_pair("Chinese", "中文"));
	ms.insert(make_pair("Chinese", "中国人"));

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

}

来实现字典的一词多意

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值