C++——STL之map和set
👀先看这里👈
😀作者:江不平
📖博客:江不平的博客
📕学如逆水行舟,不进则退
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
❀本人水平有限,如果发现有错误的地方希望可以告诉我,共同进步👍
🏐序列式容器和关联式容器
C++ 容器大致分为 2 类,序列式容器和关联式容器。
- 序列式容器,其存储的都是 C++ 基本数据类型(诸如 int、double、float、string 等)或使用结构体自定义类型的元素。数据之间没有很强的关联性,就是挨着排。
- 关联式容器存储的元素,都是一个一个的“键值对”( <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