您的位置:首页 > 其它

【深度探索STL】关联式容器set和multiset

2014-08-07 21:05 295 查看
标准的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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  STL set multiset