一、set
set的特性
- set所有元素都会根据元素的键值自动被排序
- set中的键值就是实值,实值就是键值
- 默认情况下set不允许两个元素重复
set的迭代器
- 不能根据set的迭代器改变set元素的值。因为其键值就是实值,实值就是键值,如果改变set元素值,会严重破坏set组织
- 在后面的源码中会看到,set的迭代器set<T>::iterator被定义为底层RB-tree的const_iterator。因此set的迭代器是一种constant iterators
set拥有与list的相同的某些性质
- 当客户端对它进行元素新增(insert)操作或删除(erase)操作时,操作之前的所有迭代器在操作完成之后依然有效(当然,被删除的那个元素的迭代器无效)
相关算法
- STL提供了一组set/multiset相关算法,包括交集(set_intersection)、联集(set_union)、差集(set_difference)、对称差集(set_symmetric_difference)
- 详情会在后面的“算法”文章中介绍
set的底层结构
- 由于RB-tree是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL set是以RB-tree为底层机制
- 又由于set所开放的各种操作接口,RB-tree也都提供了,所以几乎所有的set操作行为,都只是转调用RB-tree的操作行为而已
- RB-tree参阅:https://blog.csdn.net/qq_41453285/article/details/103645839
set源码
- 下面是set的源代码摘录
//代码摘录于stl_set.h template <class _Key, class _Compare, class _Alloc> class set { // requirements: __STL_CLASS_REQUIRES(_Key, _Assignable); __STL_CLASS_BINARY_FUNCTION_CHECK(_Compare, bool, _Key, _Key); public: // typedefs: typedef _Key key_type; typedef _Key value_type; typedef _Compare key_compare; typedef _Compare value_compare; private: typedef _Rb_tree<key_type, value_type, _Identity<value_type>, key_compare, _Alloc> _Rep_type; _Rep_type _M_t; // 采用红黑树来表现set public: typedef typename _Rep_type::const_pointer pointer; typedef typename _Rep_type::const_pointer const_pointer; typedef typename _Rep_type::const_reference reference; typedef typename _Rep_type::const_reference const_reference; typedef typename _Rep_type::const_iterator iterator; //注意上一行,iterator定义为RB-tree的const_iterator,表示 //set的迭代器无法执行写入操作 typedef typename _Rep_type::const_iterator const_iterator; typedef typename _Rep_type::const_reverse_iterator reverse_iterator; typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator; typedef typename _Rep_type::size_type size_type; typedef typename _Rep_type::difference_type difference_type; typedef typename _Rep_type::allocator_type allocator_type; // allocation/deallocation //注意,set一定使用RB-=tree的insert_unique()而非insert_equal() //multiset才使用RB-tree的insert_equal() set() : _M_t(_Compare(), allocator_type()) {} explicit set(const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) {} #ifdef __STL_MEMBER_TEMPLATES template <class _InputIterator> set(_InputIterator __first, _InputIterator __last) : _M_t(_Compare(), allocator_type()) { _M_t.insert_unique(__first, __last); } template <class _InputIterator> set(_InputIterator __first, _InputIterator __last, const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) { _M_t.insert_unique(__first, __last); } #else set(const value_type* __first, const value_type* __last) : _M_t(_Compare(), allocator_type()) { _M_t.insert_unique(__first, __last); } set(const value_type* __first, const value_type* __last, const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) { _M_t.insert_unique(__first, __last); } set(const_iterator __first, const_iterator __last) : _M_t(_Compare(), allocator_type()) { _M_t.insert_unique(__first, __last); } set(const_iterator __first, const_iterator __last, const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) { _M_t.insert_unique(__first, __last); } #endif /* __STL_MEMBER_TEMPLATES */ set(const set<_Key,_Compare,_Alloc>& __x) : _M_t(__x._M_t) {} set<_Key,_Compare,_Alloc>& operator=(const set<_Key, _Compare, _Alloc>& __x) { _M_t = __x._M_t; return *this; } //以下所有的set操作,RB-tree都已提供,所以set只要传递调用即可 // accessors: key_compare key_comp() const { return _M_t.key_comp(); } value_compare value_comp() const { return _M_t.key_comp(); } allocator_type get_allocator() const { return _M_t.get_allocator(); } iterator begin() const { return _M_t.begin(); } iterator end() const { return _M_t.end(); } reverse_iterator rbegin() const { return _M_t.rbegin(); } reverse_iterator rend() const { return _M_t.rend(); } bool empty() const { return _M_t.empty(); } size_type size() const { return _M_t.size(); } size_type max_size() const { return _M_t.max_size(); } void swap(set<_Key,_Compare,_Alloc>& __x) { _M_t.swap(__x._M_t); } // insert/erase pair<iterator,bool> insert(const value_type& __x) { pair<typename _Rep_type::iterator, bool> __p = _M_t.insert_unique(__x); return pair<iterator, bool>(__p.first, __p.second); } iterator insert(iterator __position, const value_type& __x) { typedef typename _Rep_type::iterator _Rep_iterator; return _M_t.insert_unique((_Rep_iterator&)__position, __x); } #ifdef __STL_MEMBER_TEMPLATES template <class _InputIterator> void insert(_InputIterator __first, _InputIterator __last) { _M_t.insert_unique(__first, __last); } #else void insert(const_iterator __first, const_iterator __last) { _M_t.insert_unique(__first, __last); } void insert(const value_type* __first, const value_type* __last) { _M_t.insert_unique(__first, __last); } #endif /* __STL_MEMBER_TEMPLATES */ void erase(iterator __position) { typedef typename _Rep_type::iterator _Rep_iterator; _M_t.erase((_Rep_iterator&)__position); } size_type erase(const key_type& __x) { return _M_t.erase(__x); } void erase(iterator __first, iterator __last) { typedef typename _Rep_type::iterator _Rep_iterator; _M_t.erase((_Rep_iterator&)__first, (_Rep_iterator&)__last); } void clear() { _M_t.clear(); } // set operations: iterator find(const key_type& __x) const { return _M_t.find(__x); } size_type count(const key_type& __x) const { return _M_t.find(__x) == _M_t.end() ? 0 : 1; } iterator lower_bound(const key_type& __x) const { return _M_t.lower_bound(__x); } iterator upper_bound(const key_type& __x) const { return _M_t.upper_bound(__x); } pair<iterator,iterator> equal_range(const key_type& __x) const { return _M_t.equal_range(__x); } #ifdef __STL_TEMPLATE_FRIENDS template <class _K1, class _C1, class _A1> friend bool operator== (const set<_K1,_C1,_A1>&, const set<_K1,_C1,_A1>&); template <class _K1, class _C1, class _A1> friend bool operator< (const set<_K1,_C1,_A1>&, const set<_K1,_C1,_A1>&); #else /* __STL_TEMPLATE_FRIENDS */ friend bool __STD_QUALIFIER operator== __STL_NULL_TMPL_ARGS (const set&, const set&); friend bool __STD_QUALIFIER operator< __STL_NULL_TMPL_ARGS (const set&, const set&); #endif /* __STL_TEMPLATE_FRIENDS */ };
set的使用案例
#include <iostream> #include <set> using namespace std; int main() { int i; int ia[5] = { 0,1,2,3,4 }; set<int> iset(ia,ia+5); std::cout << "size=" << iset.size() << std::endl; std::cout << "3 count" << iset.count(3) << std::endl << std::endl; iset.insert(3); std::cout << "size=" << iset.size() << std::endl; std::cout << "3 count" << iset.count(3) << std::endl << std::endl; iset.insert(5); std::cout << "size=" << iset.size() << std::endl; std::cout << "3 count" << iset.count(3) << std::endl << std::endl; iset.erase(1); std::cout << "size=" << iset.size() << std::endl; std::cout << "3 count" << iset.count(3) << std::endl; std::cout << "1 count" << iset.count(1) << std::endl << std::endl; set<int>::iterator ite1 = iset.begin(); set<int>::iterator ite2 = iset.end(); for (; ite1 != ite2; ite1++) std::cout << *ite1<<" "; std::cout << std::endl << std::endl; //使用STL算法来搜寻元素(循环搜索的),但是没有set的内置find函数高效 ite1 = find(iset.begin(), iset.end(), 3); if (ite1 != iset.end()) std::cout << "3 found" << std::endl; else std::cout << "3 not found" << std::endl << std::endl; //使用set的内置find函数,比STL算法高效 ite1 = iset.find(1); if (ite1 != iset.end()) std::cout << "1 found" << std::endl; else std::cout << "1 not found" << std::endl << std::endl; //*ite1 = 9; 错误,不能通过set的迭代器来改变元素的值 return 0; }
二、map
map的特性
- 所有元素都会根据元素的键值自动被排序
map中的pair结构
- map的所有元素类型都是pair,同时拥有实值(value)和键值(key)
- pair的第一个元素视为键值,第二个元素视为实值
- map不允许两个元素拥有相同的键值
- 下面是stl_pair.h中pair的定义:
//代码摘录与stl_pair.h 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) {} #ifdef __STL_MEMBER_TEMPLATES template <class _U1, class _U2> pair(const pair<_U1, _U2>& __p) : first(__p.first), second(__p.second) {} #endif };
map的迭代器
- 不可以根据map的迭代器改变节点的键值,但是可以通过map的迭代器改变节点的实值
- 因此,map iterators既不是一种constant iterators,也不是一种mutable iterators
map拥有与list的相同的某些性质
- 当客户端对它进行元素新增(insert)操作或删除(erase)操作时,操作之前的所有迭代器在操作完成之后依然有效(当然,被删除的那个元素的迭代器无效)
map的底层结构
- 由于RB-tree是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL map是以RB-tree为底层机制
- 又由于map所开放的各种操作接口,RB-tree也都提供了,所以几乎所有的map操作行为,都只是转调用RB-tree的操作行为而已
- RB-tree参阅:https://blog.csdn.net/qq_41453285/article/details/103645839
- 下面是map的结构:
map源码
- 下面是map的源代码摘录
//代码摘录与stl_map.h template <class _Key, class _Tp, class _Compare, class _Alloc> class map { public: // requirements: __STL_CLASS_REQUIRES(_Tp, _Assignable); __STL_CLASS_BINARY_FUNCTION_CHECK(_Compare, bool, _Key, _Key); // typedefs: typedef _Key key_type; //键值类型 typedef _Tp data_type; //数据(实值)类型 typedef _Tp mapped_type; typedef pair<const _Key, _Tp> value_type; //元素类型(键值/实值) typedef _Compare key_compare; //键值比较函数 //以下定义一个functor,作用就是调用“元素比较函数” class value_compare : public binary_function<value_type, value_type, bool> { friend class map<_Key,_Tp,_Compare,_Alloc>; protected : _Compare comp; value_compare(_Compare __c) : comp(__c) {} public: bool operator()(const value_type& __x, const value_type& __y) const { return comp(__x.first, __y.first); } }; private: //以下定义表述类型。以map元素类型(一个pair)的第一类型,作为RB-tree节点的键值类型 typedef _Rb_tree<key_type, value_type, _Select1st<value_type>, key_compare, _Alloc> _Rep_type; _Rep_type _M_t; // 以红黑树实现map public: typedef typename _Rep_type::pointer pointer; typedef typename _Rep_type::const_pointer const_pointer; typedef typename _Rep_type::reference reference; typedef typename _Rep_type::const_reference const_reference; typedef typename _Rep_type::iterator iterator; //注意上面一行,map不像set,map使用的是RB-tree的iterator typedef typename _Rep_type::const_iterator const_iterator; typedef typename _Rep_type::reverse_iterator reverse_iterator; typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator; typedef typename _Rep_type::size_type size_type; typedef typename _Rep_type::difference_type difference_type; typedef typename _Rep_type::allocator_type allocator_type; //注意,map一定使用底层RB-tree的insert_unique()而非insert_equal() //multimap才使用insert_equal() // allocation/deallocation map() : _M_t(_Compare(), allocator_type()) {} explicit map(const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) {} #ifdef __STL_MEMBER_TEMPLATES template <class _InputIterator> map(_InputIterator __first, _InputIterator __last) : _M_t(_Compare(), allocator_type()) { _M_t.insert_unique(__first, __last); } template <class _InputIterator> map(_InputIterator __first, _InputIterator __last, const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) { _M_t.insert_unique(__first, __last); } #else map(const value_type* __first, const value_type* __last) : _M_t(_Compare(), allocator_type()) { _M_t.insert_unique(__first, __last); } map(const value_type* __first, const value_type* __last, const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) { _M_t.insert_unique(__first, __last); } map(const_iterator __first, const_iterator __last) : _M_t(_Compare(), allocator_type()) { _M_t.insert_unique(__first, __last); } map(const_iterator __first, const_iterator __last, const _Compare& __comp, const allocator_type& __a = allocator_type()) : _M_t(__comp, __a) { _M_t.insert_unique(__first, __last); } #endif /* __STL_MEMBER_TEMPLATES */ map(const map<_Key,_Tp,_Compare,_Alloc>& __x) : _M_t(__x._M_t) {} map<_Key,_Tp,_Compare,_Alloc>& operator=(const map<_Key, _Tp, _Compare, _Alloc>& __x) { _M_t = __x._M_t; return *this; } //以下所有mao操作,RB-tree都已提供,mao只要调用即可 // accessors: key_compare key_comp() const { return _M_t.key_comp(); } value_compare value_comp() const { return value_compare(_M_t.key_comp()); } allocator_type get_allocator() const { return _M_t.get_allocator(); } iterator begin() { return _M_t.begin(); } const_iterator begin() const { return _M_t.begin(); } iterator end() { return _M_t.end(); } const_iterator end() const { return _M_t.end(); } reverse_iterator rbegin() { return _M_t.rbegin(); } const_reverse_iterator rbegin() const { return _M_t.rbegin(); } reverse_iterator rend() { return _M_t.rend(); } const_reverse_iterator rend() const { return _M_t.rend(); } bool empty() const { return _M_t.empty(); } size_type size() const { return _M_t.size(); } size_type max_size() const { return _M_t.max_size(); } _Tp& operator[](const key_type& __k) { iterator __i = lower_bound(__k); // __i->first is greater than or equivalent to __k. if (__i == end() || key_comp()(__k, (*__i).first)) __i = insert(__i, value_type(__k, _Tp())); return (*__i).second; } void swap(map<_Key,_Tp,_Compare,_Alloc>& __x) { _M_t.swap(__x._M_t); } // insert/erase pair<iterator,bool> insert(const value_type& __x) { return _M_t.insert_unique(__x); } iterator insert(iterator position, const value_type& __x) { return _M_t.insert_unique(position, __x); } #ifdef __STL_MEMBER_TEMPLATES template <class _InputIterator> void insert(_InputIterator __first, _InputIterator __last) { _M_t.insert_unique(__first, __last); } #else void insert(const value_type* __first, const value_type* __last) { _M_t.insert_unique(__first, __last); } void insert(const_iterator __first, const_iterator __last) { _M_t.insert_unique(__first, __last); } #endif /* __STL_MEMBER_TEMPLATES */ void erase(iterator __position) { _M_t.erase(__position); } size_type erase(const key_type& __x) { return _M_t.erase(__x); } void erase(iterator __first, iterator __last) { _M_t.erase(__first, __last); } void clear() { _M_t.clear(); } // map operations: iterator find(const key_type& __x) { return _M_t.find(__x); } const_iterator find(const key_type& __x) const { return _M_t.find(__x); } size_type count(const key_type& __x) const { return _M_t.find(__x) == _M_t.end() ? 0 : 1; } iterator lower_bound(const key_type& __x) {return _M_t.lower_bound(__x); } const_iterator lower_bound(const key_type& __x) const { return _M_t.lower_bound(__x); } iterator upper_bound(const key_type& __x) {return _M_t.upper_bound(__x); } const_iterator upper_bound(const key_type& __x) const { return _M_t.upper_bound(__x); } pair<iterator,iterator> equal_range(const key_type& __x) { return _M_t.equal_range(__x); } pair<const_iterator,const_iterator> equal_range(const key_type& __x) const { return _M_t.equal_range(__x); } #ifdef __STL_TEMPLATE_FRIENDS template <class _K1, class _T1, class _C1, class _A1> friend bool operator== (const map<_K1, _T1, _C1, _A1>&, const map<_K1, _T1, _C1, _A1>&); template <class _K1, class _T1, class _C1, class _A1> friend bool operator< (const map<_K1, _T1, _C1, _A1>&, const map<_K1, _T1, _C1, _A1>&); #else /* __STL_TEMPLATE_FRIENDS */ friend bool __STD_QUALIFIER operator== __STL_NULL_TMPL_ARGS (const map&, const map&); friend bool __STD_QUALIFIER operator< __STL_NULL_TMPL_ARGS (const map&, const map&); #endif /* __STL_TEMPLATE_FRIENDS */ };
- insert函数:
- 返回pair类型。pair参数1位返回的元素,第2个参数为bool,表示是否插入成功。插入成功的话pair参数1才保存返回的节点元素
- 底层交给RB-tree的insert_unique去执行
pair<iterator,bool> insert(const value_type& __x) { return _M_t.insert_unique(__x); }
- 下标运算符([]):
- 左值运用:内容可被修改
- 右值运用:内容不可被修改
map<std::string, int> simap; simap[std::string("dongshao")] = 1; //左值运用 int number = simap[std::string("dongshao")];//右值运用
- 下标运算符的定义如下
map的使用案例
#include <iostream> #include <string> #include <map> #include <utility> using namespace std; int main() { map<std::string, int> simap; simap[std::string("jjhou")] = 1; simap[std::string("jerry")] = 2; simap[std::string("jason")] = 3; simap[std::string("jimmy")] = 4; std::pair<std::string, int> value(std::string("david"), 5); simap.insert(value); map<std::string, int>::iterator simap_iter = simap.begin(); for (; simap_iter != simap.end(); simap_iter++) std::cout << simap_iter->first << ": " << simap_iter->second << std::endl; std::cout << std::endl; int number = simap[std::string("jjhou")]; std::cout << number << std::endl << std::endl; map<std::string, int>::iterator iter; iter = simap.find(std::string("mchen")); if (iter != simap.end()) std::cout << "mchen found" << std::endl; else std::cout << "mchen not found" << std::endl; iter = simap.find(std::string("jerry")); if (iter != simap.end()) std::cout << "jerry found" << std::endl; else std::cout << "jerry not found" << std::endl; std::cout << std::endl; iter->second = 9; int number2 = simap[std::string("jerry")]; std::cout << number2 << std::endl; return 0; }