标准的STL关联式容器分为set(集合)和map(映射表)两大类,以及两大类的衍生体multiset(多键集合)和multimap(多键映射表)(还有一些不在标准规格之列的关联式容器)。这些容器的底层机制均以RB-tree(红黑树)完成。
一、set
set 的特性是,所有元素都会根据元素的键值自动被排序,set 只拥有一个元素,它的键值就是实值,实值就是键值,并且set 不允许两个元素有相同的键值。
由于,set 元素值就是其键值,关系到 set 元素的排列规则,所以我们不能通过set 的迭代器来改变set 的元素值。标准的STL set 是以RB-tree 为底层机制,几乎所有的 set 操作行为,都只是转调用 RB-tree 的操作行为而已。
下面我们来看看 set 的源码(老版本)
class set { 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; //typedef RB-tree 类型 rep_type t; // 定义一棵红黑树t,也是set的唯一私有成员,set内部的元素皆存放于此 public: /*鉴于set特性,不允许修改其元素值,所以迭代器类型被定义为底层RB-tree的const_iterator*/ typedef rep_type::const_pointer pointer; typedef rep_type::const_reference reference; typedef rep_type::const_reference const_reference; typedef rep_type::const_iterator iterator; typedef rep_type::const_iterator const_iterator; typedef rep_type::const_reverse_iterator reverse_iterator; typedef rep_type::const_reverse_iterator const_reverse_iterator; typedef rep_type::size_type size_type; typedef rep_type::difference_type difference_type; // allocation/deallocation /*构造函数*/ set() : t(Compare()) {} explicit set(const Compare& comp) : t(comp) {} #ifdef __STL_MEMBER_TEMPLATES template <class InputIterator> set(InputIterator first, InputIterator last) : t(Compare()) { t.insert_unique(first, last); } template <class InputIterator> set(InputIterator first, InputIterator last, const Compare& comp) : t(comp) { t.insert_unique(first, last); } #else /*将[first,end)之间的元素插入set中*/ set(const value_type* first, const value_type* last) : t(Compare()) { t.insert_unique(first, last); } set(const value_type* first, const value_type* last, const Compare& comp) : t(comp) { t.insert_unique(first, last); } set(const_iterator first, const_iterator last) : t(Compare()) { t.insert_unique(first, last); } set(const_iterator first, const_iterator last, const Compare& comp) : t(comp) { t.insert_unique(first, last); } #endif /* __STL_MEMBER_TEMPLATES */ //赋值运算符重载 set(const set<Key, Compare, Alloc>& x) : t(x.t) {} set<Key, Compare, Alloc>& operator=(const set<Key, Compare, Alloc>& x) { t = x.t; //直接调用RB-tree的赋值操作函数 return *this; } // accessors: /*实值,键值都是同一元素值*/ key_compare key_comp() const { return t.key_comp(); } value_compare value_comp() const { return t.key_comp(); } /*下面的接口函数都是调用RB-tree的*/ iterator begin() const { return t.begin(); } iterator end() const { return t.end(); } reverse_iterator rbegin() const { return t.rbegin(); } reverse_iterator rend() const { return t.rend(); } bool empty() const { return t.empty(); } size_type size() const { return t.size(); } size_type max_size() const { return t.max_size(); } void swap(set<Key, Compare, Alloc>& x) { t.swap(x.t); } // insert/erase typedef pair<iterator, bool> pair_iterator_bool; /*插入一个元素值,返回pair类型(分别是插入元素的迭代器和是否插入成功)*/ pair<iterator, bool> insert(const value_type& x) { //RB-tree的insert_unique()返回一个pair类型 pair<rep_type::iterator, bool> p = t.insert_unique(x);//调用RB-tree的插入函数(自动排序) return pair<iterator, bool>(p.first, p.second); } /*从position开始寻找一个可以插入值为x 的元素的位置将其插入并返回其迭代器*/ //set(RB_tree)内部元素是自动排序,均不能在指定位置插入元素(或替换元素) iterator insert(iterator position, const value_type& x) { return t.insert_unique((rep_type::iterator&)position, x); } #ifdef __STL_MEMBER_TEMPLATES template <class InputIterator> void insert(InputIterator first, InputIterator last) { t.insert_unique(first, last); } #else void insert(const_iterator first, const_iterator last) { t.insert_unique(first, last); } void insert(const value_type* first, const value_type* last) { t.insert_unique(first, last); } #endif /* __STL_MEMBER_TEMPLATES */ /*删除迭代器position所指元素*/ void erase(iterator position) { t.erase((rep_type::iterator&)position); } /*删除key值为x的元素并返回被删除元素的个数*/ size_type erase(const key_type& x) { return t.erase(x); } /*删除[first,end)之间的元素*/ void erase(iterator first, iterator last) { t.erase((rep_type::iterator&)first, (rep_type::iterator&)last); } /*清空容器*/ void clear() { t.clear(); } // set operations: iterator find(const key_type& x) const { return t.find(x); } size_type count(const key_type& x) const { return t.count(x); }//统计元素值为 x的个数 /*返回一个迭代器,指向键值>=key的第一个元素*/ iterator lower_bound(const key_type& x) const { return t.lower_bound(x); } /*返回一个迭代器,指向键值>key的第一个元素*/ iterator upper_bound(const key_type& x) const { return t.upper_bound(x); } //查找键值等于x 的元素,返回pair pair<iterator, iterator> equal_range(const key_type& x) const { return t.equal_range(x); } friend bool operator==(const set&, const set&); friend bool operator<(const set&, const set&); }; /*调用RB-tree底层机制*/ template <class Key, class Compare, class Alloc> inline bool operator==(const set<Key, Compare, Alloc>& x, const set<Key, Compare, Alloc>& y) { return x.t == y.t; } template <class Key, class Compare, class Alloc> inline bool operator<(const set<Key, Compare, Alloc>& x, const set<Key, Compare, Alloc>& y) { return x.t < y.t; }set 包括下面将提到的multiset 其内部结构都是RB-tree,元素的存储以及相应操作都是引用Rb-tree的操作行为。set和multiset 内部元素都是自动排序的,这就意味着set和multiset 不提供用来直接存取元素的任何操作函数。另外对于迭代器而言,所有元素都被视为常数,这保证了不会人为的改变元素值,从而打乱既定排序。如果一定要改变元素值,必须先删除指定元素值,然后再重新插入新元素。
二、multiset
multiset 的特性以及用法和 set 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制RB-tree 的insert_equal() 而非set 采用的 insert_unique()。
从上面的源代码可以看出,set 和 multiset 的操作函数都是转而去调用RB-tree的操作函数,其具体实现都在RB-tree中。
我们知道了set 和multiset 的内部结构是RB-tree(存储方式) ,有利于我们更好的把握这两个关联式容器的函数操作。
set 的 multiset 的区别
#include <iostream> #include <set> using namespace std; int main() { int i; int ia[] = { 0, 1, 2, 3, 4 }; set<int> iset(ia, ia + 5);//set的构造函数 multiset<int> imset(ia, ia + 5); cout << "iset size = " << iset.size() << endl;//iset size = 5 cout << "iset 3 count = " << iset.count(3) << endl;//iset 3 count = 1 iset.insert(3); cout << "iset size = " << iset.size() << endl;//iset size = 5 cout << "iset 3 count = " << iset.count(3) << endl;//iset 3 count = 1 imset.insert(3); cout << "imset size = " << imset.size() << endl;//imset size = 6 cout << "imset 3 count = " << imset.count(3) << endl;//imset 3 count = 2 /*面对关联式容器,应该使用其所提供的find函数来搜寻元素*/ set<int>::iterator iter; iter = iset.find(3); if (iter != iset.end()) cout << "3 found" << endl; //*iter = 4; error,不能修改元素值 return 0; }
multiset 应用案例:最小的k个数(适合海量数据处理)
#include <iostream> #include <set> using namespace std; int main() { int m, k; int val; cin >> m >> k; multiset<int> mset; multiset<int>::iterator iter; for (int i = 0; i < m; ++i) { cin >> val; if (mset.size() < k) { mset.insert(val); } else { /*求最大的k个数,只需修改下面的迭代器和后面的if判断语句即可*/ iter = --mset.end(); if (*iter > val) { mset.erase(*iter); mset.insert(val); } } } iter = mset.begin(); for (int i = 0; i < k; ++i) { cout << *(iter++) << endl; } return 0; }