map和set,咕咕咕!
1.序列式容器和关联式容器
string,vector,list,deque,array,forward_list等是序列式容器,因为逻辑结构是线性序列,两个位置存储的值之间一般没有紧密的关联,交换后仍然是序列式容器。顺序容器中的元素是按照他们在容器中的存储位置来顺序访问和保存的。
关联式容器的逻辑结构非线性,两个位置有紧密关联,有map/set系列和unordered_map/unordered_set系列
2.set
template<class T,class Compare=less<T>,class Alloc=allocator<T>>class setset默认要求T支持小于比较,如果不支持或者想要按照自己的需求走,可以自行实现仿函数传给第二个模板参数。
set底层存储的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第三个参数
set底层用红黑树实现,增删查效率O(log N),迭代器遍历走的是搜索树的中序。
2.1 set的构造与迭代器
set支持正向和反向迭代,遍历默认按照升序,底层是二叉搜索树,中序搜索。支持迭代器意味着支持范围for,set的iterator和const_iterator都不支持迭代器修改数据,会破坏底层结构
无参默认构造
explicit set(const key_compare&comp=key_compare(),
const allocator_type& alloc=allocator_type());
迭代器区间构造
template<class InputIterator>
set(InputIterator first,InputIterator last,
const key_compare& comp=key_compare(),
const allocator_typr& alloc=allocator_type());
拷贝构造
set(const set& x);
列表构造
set(initializer_list<value_type> il,
const key_compare&comp=key_compare(),
const allocator_type& alloc=allocator_type());
迭代器是双向迭代器
iterator begin();
iterator end();
reverse_iterator rbegin();
reverse_iterator rend();
set的增删查
单个数据插入,如果已经存在则插入失败
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;set用来存数据,自动升序排序并且去重。multiset支持重复值,但是排序,find只查找中序的第一个符合的val,ms.count(x)返回x的实际个数,erase会删除所有符合的val。
3.map
template < class Key,
class T,
class Compare = less<Key>,
class Alloc = allocator<pair<const Key,T> >
> class mapKey就是map底层关键字的类型,T是map底层value的类型,set默认要求Key⽀持⼩于⽐较,如果不⽀持或者需要的话可以⾃⾏实现仿函数传给第⼆个模版参数,map底层存储数据的内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数。map底层是⽤红⿊树实 现,增删查改效率是 O(logN) ,迭代器遍历是⾛的中序,所以是按key有序顺序遍历的。
pair的介绍
typedef pair<const Key, T> value_type;
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)
{}template<class U, class V>
pair (const pair<U,V>& pr): first(pr.first), second(pr.second)
{}
};
template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{
return ( pair<T1,T2>(x,y) );
}map的支持正向和反向迭代遍历,遍历默认按key的升序顺序,因为底层是⼆叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,map⽀持修改value数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
// empty ⽆参默认构造
explicit map (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());// range 迭代器区间构造
template <class InputIterator>
map (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& = allocator_type());// copy 拷⻉构造
map (const map& x);// initializer list initializer 列表构造
map (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();map增接口,插⼊的pair键值对数据,跟set所有不同,但是查和删的接口只用关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value,同时通过迭代还可以修改value。
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>// 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
pair<iterator,bool> insert (const value_type& val);// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);// 查找k,返回k所在的迭代器,没有找到返回end()
iterator find (const key_type& k);// 查找k,返回k的个数
size_type count (const key_type& k) const;// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);// 删除k,k存在返回0,存在返回1
size_type erase (const key_type& k);// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);// 返回⼤于等k位置的迭代器
iterator lower_bound (const key_type& k);// 返回⼤于k位置的迭代器
const_iterator lower_bound (const key_type& k) const;map⽀持修改mapped_type 数据,不支持修改key数据,修改关键字数据,破坏了底层搜
索树的结构。map第⼀个支持修改的方式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map 还有⼀个非常重要的修改接口operator[],但是operator[]不仅仅支持修改,还支持插⼊数据和查找数据,所以他是⼀个多功能复合接口需要注意从内部实现⻆度,map把value值,给的是T类型,typedef为 mapped_type。⽽value_type是红⿊树结点中存储的pair键值对值
// 查找k,返回k所在的迭代器,没有找到返回end(),如果找到了通过iterator可以修改key对应的
mapped_type值
iterator find (const key_type& k);// insert插⼊⼀个pair<key, T>对象
// 1、如果key已经在map中,插⼊失败,则返回⼀个pair<iterator,bool>对象,返回pair对象
first是key所在结点的迭代器,second是false
// 2、如果key不在在map中,插⼊成功,则返回⼀个pair<iterator,bool>对象,返回pair对象
first是新插⼊key所在结点的迭代器,second是true
// 也就是说⽆论插⼊成功还是失败,返回pair<iterator,bool>对象的first都会指向key所在的迭
代器
// 那么也就意味着insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现
operator[]
// 需要注意的是这⾥有两个pair,不要混淆了,⼀个是map底层红⿊树节点中存的pair<key, T>,另
⼀个是insert返回值pair<iterator,bool>pair<iterator,bool> insert (const value_type& val);
mapped_type& operator[] (const key_type& k);
// 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;
}multimap⽀持关键值key冗余,那么insert/find/count/erase都围绕着支持关键值key冗余有所差异,这⾥跟set和multiset完全⼀样,⽐如find时,有多个key,返回中序第⼀个。其次就是multimap不⽀持[],因为⽀持key冗余,[]就只能支持插⼊了,不能支持修改。
