c++进阶之----set和map

一、set的介绍

1.概念

在C++中,std::set 是标准模板库(STL)中的一个关联容器,用于存储唯一的元素,并且这些元素会按照特定顺序自动排序

2.基本特性

1)唯一性std::set 中的元素是唯一的,不允许重复。如果尝试插入重复的元素,插入操作会失败。

2)自动排序std::set 内部会根据元素的值自动排序,排序规则默认是按照元素类型的 < 运算符来比较。也可以通过自定义比较函数来改变排序规则。

3)基于红黑树实现std::set 内部通常使用红黑树(一种自平衡的二叉搜索树)来存储元素,因此插入、删除和查找操作的时间复杂度都是 O(log n),其中 n 是容器中元素的数量。

3.代码运用

由于此处内容较为简单且知识点零碎,我将知识点放到了代码的注释里 

插入、查找的看这里: 

#include<iostream>
#include<set>
using namespace std;
int main()
{
	// 去重+升序排序
	set<int> s = { 5,9,3,6,2,3,9,8,7,4,5 };
	//支持迭代器
	//set<int>::iterator it = s.begin();
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it <<" ";
		++it;
	}
	cout << endl;
	s.insert(18);
	s.insert(2);
	s.insert(4);
	s.insert(10);
	//支持范围for
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	//插入一段initializer_list列表值,已经存在的值插入失败
	s.insert({ 6,9,6,5,10,20,30,35 });
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	// 还可以遍历string比较ascll码大小顺序遍历的
	set<string> sset = { "hello","left","right","haha" };
	for (auto e : sset)
	{
		cout << e <<" ";
	}
	cout << endl;
	//与std标准库中的find的对比
	//std标准库:暴力查找,O(N)
	auto it1 = find(s.begin(), s.end(),3);
	//set自带的:类似于二分查找,查找方式类比搜索二叉树,O(logN)
	auto it2 = s.find(3);

	return 0;
}

 单个数据删除的内容看这里:

int main()
{
	set<int> s = { 4,2,6,8,5,3,4,1,2,7 };
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	// 删除最小值
	s.erase(4);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	s.erase(s.begin());    //删除s的第一个元素,不要看你输入元素的顺序!!!
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	// 直接删除x
	int x;
	cin >> x;
	int num = s.erase(x);
	if (num == 0)
	{
		cout << x << "不存在" << endl;
	}
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	// 直接查找在利用迭代器删除x
	int y;
	cin >> y;
	auto pos = s.find(y);
	if (pos != s.end())
	{
		s.erase(pos);
	}
	else
	{
		cout << y << "不存在!" << endl;
	}
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	//用count()函数统计z的出现次数,进而判断是否存在
	int z = 0;
	cin >> z;
	if (s.count(z))
	{
		cout << x << "在!" << endl;
	}
	else
	{
		cout << x << "不存在!" << endl;
	}
	return 0;
}

 区间数据删除看这里:

int main()
{
	set<int> s = { 10,60,25,36,92,35,45,56,30,66,77,88,99 };
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	// 实现查找到的[itlow,itup)包含[30, 60]区间
	// 返回 >= 30
	auto itlow = s.lower_bound(30);
	// 返回 > 60
	auto itup = s.upper_bound(60);
	// 删除这段区间的值
	s.erase(itlow, itup);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

二、miltiset的介绍

1.概念

std::multiset 是 C++ STL 中的一个关联容器,用于存储可重复的元素,并且这些元素会根据指定的比较准则自动排序。基本用法跟set差不多,具体见代码示例

2.代码示例

int main()
{
	//multiset也是在<set>头文件中
	multiset<int> ms = { 1,2,6,5,3,2,7,8,9,7,2,7 };
	//迭代器和范围for均支持
	multiset<int>::iterator it = ms.begin();
	while (it != ms.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	// 相比set不同的是,x可能会存在多个,find查找中序的第一个
	int x;
	cin >> x;
	auto pos = ms.find(x);
	while (pos != ms.end() && *pos == x)
	{
		cout << *pos << " ";
		++pos;
	}
	cout << endl;
	//相比set不同的是,count会返回x的实际个数
	cout << ms.count(x)<<endl;
	// 相比set不同的是,erase给值时会删除所有的x
	ms.erase(x);
	for (auto e : ms)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

三、map的介绍

1.概念

在C++中,std::map 是一个非常强大的容器,属于标准模板库(STL)的一部分。它是一个基于红黑树实现的关联容器,用于存储键值对(key-value),并且保证键的唯一性。

2.基本特性:

1)键值对存储std::map 存储的是键值对,每个键(key)唯一,通过键可以快速访问对应的值(value)。

2)有序性std::map 是有序的,它会根据键的值自动排序。默认情况下,它使用键的 < 操作符来比较键的大小。

3)基于红黑树std::map 内部是通过红黑树实现的,因此插入、删除和查找操作的时间复杂度都是 O(log n)

3.代码示例

3.1 pair 的介绍

在C++中,std::pair 是一个非常实用的模板类,用于将两个不同类型的数据组合在一起。它属于标准模板库(STL)的一部分,通常用于需要同时处理两个相关数据的场景。

std::pair 提供了两个成员变量:

  • first:存储第一个数据。

  • second:存储第二个数据。

3.2 插入部分

#include<iostream>
#include<map>
using namespace std;
int main()
{
	map<string, string> m;
	//c++98
	//方法一
	pair<string, string> p1("left", "左边");
	m.insert(p1);
	//方法二
	m.insert(pair<string, string>("right", "右边"));
	//方法三
	m.insert(make_pair("up", "上"));
	//c++11
	//方法四
	m.insert({ "down","下" });
	//map<string, string>::iterator it = m.begin();
	auto it = m.begin();
	while (it != m.end())
	{
		cout << it->first << " " << it->second << endl;
		//实际上是这样的,重载->,为了可读性,省略一个->
		//cout << it.operator->()->first << " " << it.operator->()->second << endl;
		it++;
	}
	cout << endl;
	return 0;
}

插入有一个点值得注意一下: 如果插入的“键值对”在原map中已经有了,则本次插入失败,即key相同,插入失败,value相同,可以插入

我们看如下示例:

运行结果如下: 

四、运用

map这个数据结构是具有映射性质的,很多具有关联的东西都可以用map来解决,这里我们以计次问题为例,来感受一下map的妙处所在!

1.水果计次问题

现在我们给出一些水果,需要统计一下次数,这里给出代码示例

int main()
{
	string arr[] = { "苹果", "西瓜", "香蕉", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> cmap;
	for (auto& e : arr)
	{
		auto it = cmap.find(e);
		if (it == cmap.end())    //证明之前插入的没有这个水果,我现在要插入
		{
			cmap.insert({e,1});
		}
		else  //证明之前插入过这个水果了,次数增加一次即可
		{
			it->second++;
		}
	}
	for (auto e : cmap)
	{
		cout << e.first << " " << e.second << endl;
	}
	return 0;
}

这种方法是大家都能想到的,但是可能代码要写好长,感觉费时间,因此我们改进一下

 2.[ ]的介绍

std::map[] 运算符是一种非常重要的成员函数,用于访问或插入元素。

1)如果 key 已经存在于 map 中,[] 运算符会返回对应键的值的引用。

2)如果 key 不存在,[] 运算符会插入一个新的键值对,键为 key,值为 ValueType 的默认构造值(例如,如果 ValueTypeint,则默认值为 0),并返回这个新值的引用。

3.【】的应用

	map<string, string> dict;
	// 插入(基本不会这样用)
	dict["left"];
	// 插入+修改
	dict["up"] = "上";
	for (auto e : dict)
	{
		cout << e.first << " " << e.second << endl;
	}
	cout << endl;
	// 修改
	dict["left"] = "左边";
	// 查找,输出对应的值
	cout << dict["left"] << endl;
	cout << dict["up"] << endl;

运行结果如下 

4.代码更改

int main()
{
	string arr[] = { "苹果", "西瓜", "香蕉", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> cmap;
	for (auto e : arr)
	{
		cmap[e]++;
	}
	for (auto e : cmap)
    {
	    cout << e.first << " " << e.second << endl;
    }
	return 0;
}

说明:在上述代码中,如果我的水果已经存在了,那么【】会返回对应水果(键)的次数(值)的引用,并通过++完成自增,如果我的水果在之前是没有出现过的,那cmap中会插入一个新的键值对,键为 水果名称,值为 次数,并返回这个新值的引用,在通过++完成自增计数

我们可以在插入几个水果试一试:

	pair<map<string, int>::iterator, bool>  r1 = cmap.insert({ "草莓", 1 });
	pair<map<string, int>::iterator, bool>  r2 = cmap.insert({ "西瓜", 1 });
	pair<map<string,int>::iterator, int> r3 = cmap.insert({ "蓝莓",10 });
	for (auto e : cmap)
	{
		cout << e.first << " " << e.second << endl;
	}

 五、multimap

multimap与map的区别我们可以类比mutiset和set的区别,这里我们不多说了,直接看一下代码!

int main()
{
	map<string, string> ant;
	ant.insert({ "black","white" });
	ant.insert({ "dirty","clean" });
	ant.insert({ "dirty","tidy" });
	ant.insert(pair<string, string>("dirty", "neat"));
	for (auto e : ant)
	{
		cout << e.first << "<->" << e.second << endl;
	}
	cout << endl;
	multimap<string, string> mant;
	mant.insert({ "black","white" });
	mant.insert({ "dirty","clean" });
	mant.insert({ "dirty","tidy" });
	mant.insert(pair<string, string>("dirty", "neat"));
	//以上两种方式都可以插入,但个人更倾向于隐式类型转换(第一种)
	for (auto e : mant)
	{
		cout << e.first << "<->" << e.second << endl;
	}
	return 0;
}

 结果如下:

下面这个大家看一下就好,这里不在多说了

int main()
{
	std::multimap<char, int> mymm;

	mymm.insert(std::pair<char, int>('a', 10));
	mymm.insert(std::pair<char, int>('b', 20));
	mymm.insert(std::pair<char, int>('b', 30));
	mymm.insert(std::pair<char, int>('b', 40));
	mymm.insert(std::pair<char, int>('c', 50));
	mymm.insert(std::pair<char, int>('c', 60));
	mymm.insert(std::pair<char, int>('d', 60));

	std::cout << "mymm contains:\n";
	for (char ch = 'a'; ch <= 'd'; ch++)
	{
		std::pair <std::multimap<char, int>::iterator, std::multimap<char, int>::iterator> ret;
		ret = mymm.equal_range(ch);
		std::cout << ch << " =>";
		for (std::multimap<char, int>::iterator it = ret.first; it != ret.second; ++it)
			std::cout << ' ' << it->second;
		std::cout << '\n';
	}

	return 0;
}

 结果如下:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值