一.map和set的使用
1.序列式容器和关联式容器
序列式容器是线性容器,两个位置存储的值一般没有紧密的关联联系,比如STL:string,vector,list,deque等
关联式容器是非线性容器,两个位置存储的值有紧密的关联联系,比如map,set,哈希表
二.set的使用
2.1 map和set底层是红黑树(平衡二叉搜索树)(logN)
文档里面有介绍set的用法
2.2 set的声明(T是关键字类型)
cpp
template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;
2.3 set的构造和迭代器
set支持双向迭代器,默认是升序,底层是二叉搜索树中序遍历,所以是有序的
set的构造
cpp
// empty (1) ⽆参默认构造
explicit set (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
// range (2) 迭代器区间构造
template <class InputIterator>
set (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& = allocator_type());
// copy (3) 拷⻉构造
set (const set& x);
// initializer list (5) initializer 列表构造
set (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
// 迭代器是⼀个双向迭代器
iterator -> a bidirectional iterator to const value_type
// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend()
2.4 set的增删查
cpp
Member types
key_type -> The first template parameter (T)
value_type -> The first template parameter (T)
// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
// 查找val,返回val所在的迭代器,没有找到返回end()
iterator find (const value_type& val);
// 查找val,返回Val的个数
size_type count (const value_type& val) const;
// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);
// 删除val,val不存在返回0,存在返回1
size_type erase (const value_type& val);
// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);
// 返回⼤于等val位置的迭代器
iterator lower_bound (const value_type& val) const;
// 返回⼤于val位置的迭代器
iterator upper_bound (const value_type& val) const;
1.set的底层是平衡二叉搜索树,在删除的时候有直接删除比如:根节点3(迭代器失效),替代法删除:比如根节点12,那其实是13替代了原来12的位置(但是原来12的迭代器还是失效)
2.set.find(x)的时间复杂度是O(log(N)),是走高度次,算法库里面的find是O(N)
3.set.count(x)返回的是x元素的数量,set本身只有一个x,返回数量是与multiset对应,也就是可重复的升序集合

4.lower_bound和upper_bound
给定一个数x,lower_bound返回的是第一个>=x的迭代器,upper_bound返回的是>x的迭代器.
2.5multiset和set的差异
multiset和set的使⽤基本完全类似,主要区别点在于multiset⽀持值冗余,那么 insert/find/count/erase都围绕着⽀持值冗余有所差异
相⽐set不同的是,multiset是排序,但是不去重
在find查找的时候,找到的是中序(左根右)的第一个

例题1:https://leetcode.cn/problems/intersection-of-two-arrays
例题2:https://leetcode.cn/problems/c32eOV
三.map
3.1 认识pair
1.the first是key,second是value
2.make_pair(key,value)就不用pair<T1,T2>(key,value);自动匹配类型

3.插入数据时只关注key,key相等就不会插入,与value无关.
4.the first是const类型的,不支持修改key但是value可以修改,second可以修改
5.比较是先比较first,再second
3.2map::insert

从文档里面可以看到返回的是一个pair<iterator,bool>,就是插入位置和查找功能
插入成功:pair<新插入值所在的迭代器,true成功插入>
插入失败:pair<与新插入值相等的值的迭代器,false插入失败>
3.3 map::operator[]
1.这里的[]不是vector等传统意义上的数组访问的[]
cpp
// operator的内部实现
mapped_type& operator[] (const key_type& k)
{
// 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊+修改功能
// 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的
迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能
pair<iterator, bool> ret = insert({ k, mapped_type() });
iterator it = ret.first;
return it->second;
}
2.operator底层是使用insert实现的,insert在失败的时候会变成查找功能
cpp
//key不存在->插入{"insert",string()}
dict["insert"];//缺省
//key不存在->插入+修改
dict["left"]="左边"
//查找,确定key存在
cout<<dict["left"]<<endl;
//插入,因为sort不存在
cout<<dict["sort"]<<endl;
3.4multimap和map的差异
multimap和map的使⽤基本完全类似,主要区别点在于multimap⽀持关键值key冗余,那么 insert/find/count/erase都围绕着⽀持关键值key冗余有所差异,这⾥跟set和multiset完全⼀样,⽐如 find时,有多个key,返回中序第⼀个。其次就是multimap不⽀持[],因为支持key冗余,[]就只能支持插⼊了,不能⽀持修改。
例题1:
例题2:https://leetcode.cn/problems/g5c51o?q=%E5%89%8Dk%E4%B8%AA
stable_sort,稳定排序,不改变相对位置