map与set详解

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

一、两个概念

序列是容器:
在初阶阶段,已经接触STL的部分容器,vector,list,deque,forward_list等等,这些都哦是序列是容器,因为其底层为线性序列的数据结构,里面存的是元素本身,那么什么是关联式容器?

关联式容器:
也是用来存储数据的,与序列式容器不同的是,其里面村的是<Key,Value>这样结构的键值对,在数据检索时效率更高

二、set

在这里插入图片描述

①set的两种遍历方式

首先,set是Key的模型,不允许修改,所以set只有增删查

//初始化
set<int> s={1,2,1,6,3,8,5};
//可以这样进行初始化,这个是C++11中的列表初始化
//initializer_list
int a[] = (1,2,1,6,3,8,5);
set<int>s(a,a+sizeof(a)/sizeof(int));

//第一种
set<int>::iterator  it = s.begin();
while(it!=s.end())
{
	cout<<*it;
	it++;
}

//第二种
for(auto k:s)
{
	cout<<k;
}

但是范围for是用迭代器实现的,所以这严格来说只能算是一种遍历方式

set的地曾是一可红黑树(红黑树也是搜索二叉树),set间接可以完成的任务就是排序+去中,然后真正干的事就是去找在不在

迭代器也是一种设计模式,访问容器,在没有暴露容器的底层结构的前提下提供了统一的访问方式,哪怕他是红黑树,所以迭代器是很牛的,这也让他成为了STL六大组件之一

②set的erase

//1.
s.erase(x);//某个数

//2.
auto pos = s.find(x);//找到某个数位置
s.erase(pos);

需要注意的是,我们在实际使用过程当中,我们需要把所有容器的erase都当作迭代器已经失效来看待,每一次的erase都要重新去更新迭代器的位置。避免迭代器失效而奔溃

//1.erase提供下一个位置迭代器,直接用
while(...)
{
	pos = s.erase(x);
}
//2.不断的find
pos = s.find(x);
s.erase(pos);
pos = s.find(y);
...

③set的count

count的作用是计数set当中的该键值个数,但是搜索二叉树/红黑树本来就不允许键值冗余,set当中留一个count来干嘛,结果不都是0/1吗,我为什么不用find?
因为count是为了接口一致性的问题,除了set外,还有一个multimap,它是允许键值冗余的

三、map

在这里插入图片描述

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

②map的insert

map需要插入的是一个pair结构体,底层也是红黑树

//插入方法
map<string,string> dict;
//1.
pair<string,string> kv1("sort","排序");
dict.insert(kv1);
//2.
dict.insert(pair<string,string>("zhupi","帅哥"));
//3.
typedef pair<string,string> DictKV;
dict.insert(DictKV("zhupi","帅哥"));
//4.make_pair
dict.insert(make_pair("zhupi","帅哥"));
//5.列表初始化
dict.insert({"zhupi","帅哥"});

而对于make_pair,其实就是一个函数模板,优势就是自动推导,需要包含头文件utility,实用工具
在这里插入图片描述
而且呢,这种短代码一般定义成内联,在预编译阶段就会被替换

③访问键值对

//map<string,string>::iterator it = dict.begin();
auto it = dict.begin();
//错误写法
while(it!=dict.end())
{
	cout<<*it<<" ";
	++it;
}
cout<<endl;

上面这样写是不行的,通过上面键值对pair的定义我们可以看出,他是一个结构体,但是ostream类中并没有重载关于pair<string,string>的重载。所以我们需要通过以下方式进行访问

//1.
while(it!=dict.end())
{
	cout<<(*it).first<<(*it).second<<endl;
	++it;
}

//2.先看下面的operator->
while(it!=dict.end())
{
	cout<< it -> first << it -> second <<endl;
	++it;
}

④map的operator->

Ptr/*模板参数*/ operator->()
{
	return &(*this);
}

这一步的操作是为了从迭代器转换为单纯的指针,因为迭代器是一层封装,我们无法拿到底层的结。
而当我们使用it->first的时候,它其实是it->->first,第一个代表转换为纯指针,第二个才代表拿到pair中的first,只不过编译器为了可读性把他弄成一个

⑤map的operator[]

在这里插入图片描述
map的[]已经有点违背之前使用string和vector的伊斯兰,之前的目的的下标随机访问,得益于底层空间的连续,但是map是红黑树

operator[]的作用是通过Key找到对应的Value
1.map中没有这个key,则插入一个pair<Key,V()>,后面的是value的匿名对象,因为V可能是自定义类型,所以int也有了int(),默认是0.插入之后,返回Value的引用
2.map中有这个key,返回value的引用

我们来看看库中的写法

//已简化
V& operator[](const K& key)
{
    pair<iterator,bool> ret=insert(make_pair(Key,Value));
    //因为insert返回的是一个pair<iterator,bool>;
    //insert返回的是一个pair<iterator,bool>
    //第一个参数的second就是value,
    //第二个参数代表是新插入true还是本来就在false
    return ret.first->second;
    //ret是一个pair结构体,ret.first就是迭代器,然后(*(ret.first)).second或者(ret.first)->second
}

⑥map的at

at与operator[]来说,就少了一个功能
at只能够查找+修改
也就是说,如果key在,那么返回value的引用
如果不再,那么我就抛异常

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪皮兄弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值