map和set基本使用
- 二叉搜索树
- set
- multiset
- map
- multimap
二叉搜索树
在学习map和和set的使用之前,我们需要对二叉搜索树有一定的了解。
二叉搜索树也称二叉排序树或者二叉查找树,如果该树不是空树,就应该满足以下条件:非空左子树的所有键值都小于其根结点的键值,非空右子树的所有键值都大于其根结点的键值,且左右子树也都是二叉搜索树。
二叉搜索树的插入和查找逻辑都比较简单,这里不过多讨论,其结点的删除一般采用替换删除法,进行删除时分4种情况:
1.要删除的结点没有孩子
直接删除并将其父结点指向置空即可
2.要删除的结点只有左孩子
将其父结点的指向指向要删除的结点的左孩子后直接删除
3.要删除的结点只有右孩子
将其父结点的指向指向要删除的结点的右孩子后直接删除
4.要删除的结点有左孩子、右孩子
在要删除结点的左子树找到键值最大的结点L或在要删除结点的右子树找到键值最小的结点R,只需要将L或R的键值赋值给要删除的结点,然后删除L或R结点即可,因为L一定没有右孩子,R一定没有左孩子,所以L或R结点的删除一定属于1、2、3情况中的某一种。
由于删除除叶子结点时叶子结点既可以看成只有左孩子(左孩子为空)也可以看成只有右孩子(右孩子为空),所以可以将1、2或者1、3当作同一类情况来看。
二叉搜索树有两种模型:
1.K模型:即只有key作为关键码,结点只需要存储key即可,key就是我们需要的数据。
2.KV模型:每一个关键码key都对应一个只value,我们需要存储key和value,通过key去搜索结点时,value才是我们需要的数据。
使用二叉搜索树,其数据的插入、删除、查找成本都较低,是良好的数据结构,但当其变为单支时,查找效率会大大下降,从而导致插入和删除的效率也下降,由此改进就产生了平衡搜索二叉树,使进行结点插入删除时这棵树依然接近完全二叉树,经典的平衡搜索二叉树有AVL树和红黑树。
我们前面使用的vector、list等容器都是属于序列式容器,其底层为线性结构,里面存储的是元素本身,而关联式容器存储的是<key value>结构的键值对,在进行数据检索时效率比序列式容器高。我们下面介绍的map、multimap、set、multiset属于树形结构的关联式容器,其底层结构为红黑树。
在学习使用这些容器之前,我们先了解2个模板
1.pair
pair是一个类模板,包含在头文件<utility>中
template <class T1, class T2> struct pair;
这个类模板实例化后可以存储两个类型不同的数据,存放在公共成员变量first和second中,在类外可以直接访问,其构造函数如下:
pair();template<class U, class V> pair (const pair<U,V>& pr);pair (const first_type& a, const second_type& b);//
//Type of member first, aliased as first_type.//Type of member second, aliased as second_type.
2.make_pair
make_pair是一个函数模板,返回一个pair对象,包含在头文件<utility>中
template <class T1, class T2>pair<T1,T2> make_pair (T1 x, T2 y);
set
set在用户使用上看上去是K型搜索二叉树,只存放了key值,但在底层存放的是<key,key>构成的键值对,不过我们用户直接将其当成K型搜索二叉树即可,不需要构建键值对。需要注意的是,set的元素比较默认是按小于进行比较的,且不会插入重复元素,不能修改结点值。
template < class T, // set::key_type/value_typeclass Compare = less<T>, // set::key_compare/value_compareclass Alloc = allocator<T> // set::allocator_type> class set;
1.构造
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_type& alloc = allocator_type());set (const set& x);// InputIterator shall be an input iterator type that points to elements of a type from which value_type objects can be constructed.
2.赋值运算符重载
set& operator= (const set& x);
3.迭代器
//正向迭代器iterator begin();
const_iterator begin() const;iterator end();
const_iterator end() const;//反向迭代器reverse_iterator rbegin();
const_reverse_iterator rbegin() const;reverse_iterator rend();
const_reverse_iterator rend() const;
4.判断是否为空
bool empty() const;
5.获取结点个数
size_type size() const;
6.元素插入
pair<iterator,bool> insert (const value_type& val);
//如果元素是第一次插入,pair的first指向插入的结点,bool设为true
//如果相同key值已经存在,pair的first指向已经存在的结点,bool设为falseiterator insert (iterator position, const value_type& val);template <class InputIterator>void insert (InputIterator first, InputIterator last);
7.元素删除
void erase (iterator position);size_type erase (const value_type& val);void erase (iterator first, iterator last);
8.清空搜索树
void clear();
9.交换两颗树
void swap (set& x);
10.查找某个结点
iterator find (const value_type& val) const;
11.获取某一key值的结点个数
size_type count (const value_type& val) const;
//由于set相同key值的结点只存一个,所以只返回0或1
12.返回第一个大于等于key值的结点
iterator lower_bound (const value_type& val) const;
13.返回第一个大于key值的结点
iterator upper_bound (const value_type& val) const;
multiset
multiset的元素允许重复,需要包含<set>头文件,multiset在底层存放的也是<key,key>构成的键值对,不过我们用户直接将其当成K型搜索二叉树即可,不需要构建键值对,其元素比较默认是按小于进行比较的,不能修改结点值。multiset容器的使用几乎与set一样,我们这里只看常用的有区别的接口。
1.插入
iterator insert (const value_type& val);
//返回结点的迭代器,不再是pairiterator insert (iterator position, const value_type& val);template <class InputIterator>void insert (InputIterator first, InputIterator last);
2.查找
iterator find (const value_type& val) const;
//如果存在重复元素,也只返回其中一个结点
3.返回key值的结点个数
size_type count (const value_type& val) const;
//不再只是返回0或1
map
map是一种KT型搜索二叉树,在底层存的是<key value>键值对元素比较默认是按小于进行比较的,可以修改value值,但不能修改key值,不允许元素重复(key值相等即为元素相等),如果元素插入时key值相等,但value值不同,不进行任何操作。
template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare = less<Key>, // map::key_compareclass Alloc = allocator<pair<const Key,T> > // map::allocator_type> class map;
1.构造
explicit map (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());template <class InputIterator>map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());map (const map& x);//c++11支持使用pair对象构造map对象
2.赋值运算符重载
map& operator= (const map& x);//c++11支持使用pair对象进行赋值
3.迭代器
//正向迭代器iterator begin();
const_iterator begin() const;iterator end();
const_iterator end() const;//反向迭代器reverse_iterator rbegin();
const_reverse_iterator rbegin() const;reverse_iterator rend();
const_reverse_iterator rend() const;
4.判断是否为空
bool empty() const;
5.获取结点个数
size_type size() const;
6.插入
pair<iterator,bool> insert (const value_type& val);
//如果元素是第一次插入,pair的first指向插入的结点,bool设为true
//如果相同key值已经存在,pair的first指向已经存在的结点,bool设为falseiterator insert (iterator position, const value_type& val);template <class InputIterator>void insert (InputIterator first, InputIterator last);//c++11支持使用pair对象进行插入
7.删除
void erase (iterator position);size_type erase (const key_type& k);void erase (iterator first, iterator last);
8.[]运算符重载
mapped_type& operator[] (const key_type& k);
//利用k值作为下标获取对应的value值的引用
//如果k为key值的结点不存在,会插入key=k,value=默认构造的键值对,然后返回value的引用
//其本质是这个:(*((this-insert(make_pair(k,mapped_type()))).first)).second
9.at下标访问
mapped_type& at (const key_type& k);
const mapped_type& at (const key_type& k) const;
//找到匹配的k值,返回对应的引用,找不到就报错
10.查找某个结点
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
11.获取某一key值的结点个数
size_type count (const value_type& val) const;
//由于set相同key值的结点只存一个,所以只返回0或1
12.返回第一个大于等于key值的结点
iterator lower_bound (const key_type& k);
const_iterator lower_bound (const key_type& k) const
13.返回第一个大于key值的结点
iterator upper_bound (const key_type& k);
const_iterator upper_bound (const key_type& k) const;
14.交换两颗树
void swap (map& x);
15.清空搜索树
void clear();
multimap
multimap的元素允许重复,需要包含<map>头文件,其元素比较默认是按小于进行比较的,不能修改结点值。multimap容器的使用几乎与map一样,我们这里只看常用的有区别的接口。
由于key值可以重复,所以multimat不支持[]下标访问和at下标访问
1.插入
iterator insert (const value_type& val);
//也是返回迭代器,不再是pair对象iterator insert (iterator position, const value_type& val);template <class InputIterator>void insert (InputIterator first, InputIterator last);
2.查找
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
//如果存在重复元素,也只返回其中一个结点
3.返回key值的结点个数
size_type count (const value_type& val) const;
//也不再只是返回0或1