C++——STL之map和set

👀先看这里👈
😀作者:江不平
📖博客:江不平的博客
📕学如逆水行舟,不进则退
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
❀本人水平有限,如果发现有错误的地方希望可以告诉我,共同进步👍

🏐序列式容器和关联式容器

C++ 容器大致分为 2 类,序列式容器和关联式容器。

  1. 序列式容器,其存储的都是 C++ 基本数据类型(诸如 int、double、float、string 等)或使用结构体自定义类型的元素。数据之间没有很强的关联性,就是挨着排。
  2. 关联式容器存储的元素,都是一个一个的“键值对”( <key,value> )。如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式,具有很强的关联性。也是因为这个特性,数据的增删查改相比于序列式容器来说效率更高。

🏀什么是键值对

是一种具有一一对应关系的结构,这种结构有两个成员变量,一个是键值key,另一个是与key对应的value

在SGI-STL中关于键值对的定义:

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

C++ STL 标准库提供了 4 种关联式容器,分别为 map、set、multimap、multiset。他们的底层结构都是红黑树,下面来看一下这些关联式容器吧。

🏐什么是set

以前学的一般都是两个参数,set是三个
在这里插入图片描述
可以看到这里给了一个Compare,和以前的学习优先级队列时的仿函数差不多,可以自己去写定规则去比较,比如T是自定义类型的时候,或者类如Date*这样的,自己需要去写一个比较规则,一个仿函数。
来看一下它的构造:
在这里插入图片描述
有一个全缺省构造,可以看到在构造这里也传这个仿函数进去,还有一个迭代区间构造,一个拷贝构造
这里拷贝构造是深拷贝,要拷贝一棵树,代价极大,所以如果不少特别需要,要谨慎拷贝

key模型支持增删查不支持改,因为本质是一棵搜索树,修改了就乱了。

void test_set()
{
   
	/*set<int> s;
	s.insert(4);
	s.insert(2);
	s.insert(1);*/
	//我们还可以迭代器区间初始化,如果底层是连续的物理空间,那么原生指针就可以当作天然的迭代器

    //当然还可以像下面这么写,list,vector都可以这么写,这是C++11里增加的列表初始化
	/*set<int> s = { 3, 2, 1, 6, 3, 7, 5 };*/
	
	我们可以用迭代器区间初始化如下面:
	int a[] = {
    3, 2, 1, 6, 3, 7, 5 };
	set<int> s(a, a + sizeof(a) / sizeof(int));

这里set直接完成了排序和去重的功能,去重是间接作用出来的,因为这个地方3在第二次插入的时候发现前面插入过了,所以就没有继续插入。

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

	for (auto e : s)//可以使用迭代器就可以使用范围for
	{
   
		cout << e << " ";
	}
	cout << endl;

如果说我们想实现降序的排序,我们怎么可以实现呢?第一种我们可以用反向迭代器,第二种我们可以直接去改变树,我们在初始化插入的时候,迭代器区间去初始化的时候我们这么写(用greater,但是在前面我们要加上#include< function > ):

	set<int, greater<int>> s(a, a + sizeof(a) / sizeof(int));

我们可以看到迭代器在此处的作用,这是一种设计模式,它访问list,map,set,vector容器,没有暴露容器的底层结构,迭代器的优点在于不需要关心底层实现,这是一种封装的体现,你不需要去关心它是链表,顺序表还是说是树,用一种方式做到全都访问。

构造主要支持的核心方式是
那我们现在遍历它主要支持的核心方式是什么呢

🏀erase

erase往往与find分不开,那么下面这两种删除方式有区别吗?

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

	set<int>::iterator pos = s.find(3);
	s.erase(pos);
	for (auto e : s)
	{
   
		cout << e << " ";
	}
	cout << endl;

答:都是删除3的话在这个序列s上是没区别的。但是如果是下面这样就得出的效果就不一样了

    s
### C++ STL 中 `map` `set` 的使用方法差异 #### 使用方法 对于 `map` 容器,在定义之后可以插入键值对并遍历输出这些元素。下面是一个简单的例子来展示如何创建、插入以及迭代访问一个 `map`: ```cpp #include <iostream> #include <string> #include <map> using namespace std; int main() { map<string, float> m; // 插入元素,按键值的由小到大放入红黑树中 m["Jack"] = 98.5f; m["Bomi"] = 96.0f; m["Kate"] = 97.5f; for (auto& pair : m) { cout << pair.first << ": " << pair.second << endl; } return 0; } ``` 而针对 `set` 容器,则主要用于存储唯一的关键字,并且也支持类似的遍历操作。这里给出一段用于初始化打印 `set` 集合内所有成员的小程序片段[^2]: ```cpp for (auto it = mySet.begin(); it != mySet.end(); ++it) { std::cout << *it << std::endl; } ``` #### 差异分析 虽然两者均采用红黑树作为内部结构实现自动排序特性,但是它们之间存在一些显著的不同点。 - **数据组织形式**:`set` 只保存单一类型的对象,即只包含关键字;相反地,`map` 则是由一对关联起来的数据构成——一个是充当索引作用的关键字(key),另一个则是对应的值(value)[^1]。 - **查找性能**:由于 `map` 是通过 key 来快速定位 value 的关系型容器,因此当需要频繁查询特定记录时更为高效;相比之下,如果仅仅是想要维护一组不重复项列表的话,那么 `set` 就足够了[^3]。 - **内存占用情况**:通常情况下,因为每条记录都需要额外储存其相联系的信息,所以相同数量级下 `map` 所消耗的空间会比 `set` 多一点。 综上所述,在决定选用哪种容器之前应当充分考虑应用程序的具体需求,从而做出最合适的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江不平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值