【STL】关联式容器 - Set和Multiset
Set和Multiset
STL提供的vector,list和deque属于序列式容器即可序群集,每个元素均有固定的位置,取决于插入时机和地点,和元素值无关。如果以追加的方式对一个群集置入六个元素,它们的排列次序将和置入次序一致。
而关联式容器为已序群集,元素位置取决于特定的排序准则。如果讲六个元素置入群集中,它们的位置取决于元素值,与插入次序无关。STL提供了四个关联式容器:set,multiset,map和multiset。
set和mulitisets会根据特定的排序准则,自动将元素排序。两者不同点在于multisets允许元素重复,而set不允许。
set和multiset的结构:

在使用set或multiset前,必须先引入头文件
#include <set>
在这个头文件中,上述两个型别都被定义为命名空间std内的class templates:
namespace std{
template <class T,
class Compare = less<T>,
class Allocator = allocator<T> >
class set;
template <class T,
class Compare = less<T>,
class Allocator = allocator<T> >
class multiset;
}
Set和Multiset的能力
与所有标准关联式容器类似,set和multiset通常以平衡二叉树完成。
Set和Multisets的内部结构:

自动排序的主要优点在于使用二叉树搜寻元素时具有良好的性能。搜寻函数算法具有对数的复杂度。在拥有1000个元素的set或multiset中搜寻元素,二叉树搜寻动作的平均时间为线性搜寻事件的1/50。
但是自动排序造成set和multiset的一个重要限制:不能直接改变元素值,因此,要改变元素值,必须先删除旧元素,再插入新元素。
- set和multiset不提供用来存取元素的任何操作函数
- 通过迭代器进行元素间接存取
Set和Multiset的操作函数
set和multiset的构造和析构函数
| 操作 | 效果 |
|---|---|
| set c | 产生一个空的set/multiset,其中不含任何元素 |
| set c(op) | 以op为排序准则,产生一个空的set/multiset |
| set c1(c2) | 产生某个set/multiset的副本,所有元素均被复制 |
| set c(begin,end,op) | 以区间[begin,end]内的元素产生一个set/multiset |
| c.~set() | 销毁所有元素,释放内存 |
非变动性操作(查询大小,相互比较)
| 操作 | 效果 |
|---|---|
| c.size() | 返回容器的大小 |
| c.empty() | 判断容器大小是否为空 |
| c.max_size() | 返回可容纳的最大元素数量 |
| c1 == c2 | 判断c1是否等于c2 |
| c1 != c2 | 判断是否c1不等于c2 |
元素比较动作只能用于型别相同的容器,否则编译阶段就会报错。
特殊的搜寻函数
set和multiset在元素快速搜寻方面有优化设计,所以提供了特殊的搜寻函数,使用这些优化算法,可以获得对数复杂度,由于非STL算法的线性复杂度
| 操作 | 效果 |
|---|---|
| count(elem) | 返回"元素值为elem"的元素个数 |
| find(elem) | 返回"元素值为elem"的第一个元素。如果找不到就返回end() |
| lower_bound(elem) | 返回elem的第一个可安插位置,即"元素值>=elem"的第一个元素位置 |
| upper_bound(elem) | 返回elem的最后一个可安插位置,即"元素值>elem"的第一个元素位置 |
| equal_range(elem) | 返回elem的可安插的第一个位置和最后一个位置,即"元素值==elem"的元素区间 |
实例:
#include <set>
#include <iostream>
using namespace std;
int main()
{
set<int> c;
c.insert(1);
c.insert(2);
c.insert(3);
c.insert(4);
c.insert(5);
cout<<"lower_nound(3):" << *c.lower_bound(3) <<endl;
cout<<"upper_bound(3):" << *c.upper_bound(3) <<endl;
cout<< "c.equal_range(3):" << *c.equal_range(3).first << " "
<< *c.equal_range(3).second <<endl;
cout<<endl;
cout<<"lower_nound(5):" << *c.lower_bound(5) <<endl;
cout<<"upper_bound(5):" << *c.upper_bound(5) <<endl;
cout<< "c.equal_range(5):" << *c.equal_range(5).first << " "
<< *c.equal_range(5).second <<endl;
}
运行结果:

赋值
在赋值的操作函数中,赋值操作的两端容器必须具有相同型别
| 操作 | 效果 |
|---|---|
| c1 = c2 | 将c2中所有元素赋值给c1 |
| c1.swap(c2) | 将c1和c2元素互换 |
| swap(c1,c2) | 同上。属于全局函数 |
迭代器相关函数
set和multiset不提供元素直接存取,所以只能采用迭代器。
| 操作 | 效果 |
|---|---|
| c.begin() | 返回一个双向迭代器,指向第一个元素 |
| c.end() | 返回一个双向迭代器,指向最后元素的下一位置 |
| c.rbegin() | 返回一个逆向迭代器,指向逆向遍历时的第一个元素 |
| c.rend() | 返回一个逆向迭代器,指向逆向遍历时的最后元素的下一位置 |
这里的迭代器是双向迭代器,所以对于只能用于随机存取迭代器的STL算法,set和multiset就无法使用了
元素的安插和移除
按照STL惯例,必须保证参数有效,迭代器必须指向有效位置,序列起点不能位于终点之后,不能从空容器中删除元素
插入和移除多个元素时,单一调用(一次处理)比多次调用(逐一处理)快得多
| 操作 | 效果 |
|---|---|
| c.insert(elem) | 插入一份elem副本,返回新元素位置 |
| c.insert(pos,elem) | 在pos位置插入一份elem副本,返回新元素位置 |
| c.insert(beg,end) | 在区间[beg,end]内所有元素的副本安插到c |
| c.erase(elem) | 移除"与elem相等的所有元素",返回被移除的元素个数 |
| c.erase(pos) | 移除迭代器pos位置的元素,无返回值 |
| c.erase(beg,end) | 移除区间[beg,end]内的所有元素,无返回值 |
| c.clear() | 移除全部元素,将整个容器清空 |
插入函数的返回值型别不完全相同:
- set提供接口:
pair<iterator,bool> insert(const value_type& elem);
iterator insert(iterator pos_hint,const value_type& elem);
iterator erase(iterator pos);
iterator erase(iterator beg,iterator end);
- multiset提供接口:
iterator insert(const value_type& elem);
iterator insert(iterator pos_hint,const value_type& elem);
void erase(iterator pos);
void erase(iterator beg,iterator end);
返回值类型不同的原因是,mulitisets允许元素重复,而set不允许。所以如果将某元素安插至一个set内,该set已经内含同值元素,则安插操作失败。
异常处理
set和multiset是以节点为基础的容器,如果节点构造失败,容器仍保持原样。此外由于析构函数并不抛出异常,所以节点的移除不可能失败。
set和multiset实例:
展示set的一些能力
#include <iostream>
#include <set>
#include <iterator>
using namespace std;
int main()
{
// 定义一个set,容纳降序排列的int
typedef set<int,greater<int> > IntSet;
IntSet coll1;
// 插入元素到random order
coll1.insert(4);
coll1.insert(3);
coll1.insert(5);
coll1.insert(1);
coll1.insert(6);
coll1.insert(2);
coll1.insert(5); // 此插入动作会被忽略,因为set不允许数值重复的元素
// iterate over all elements and print them
IntSet::iterator pos;
for(pos = coll1.begin();pos != coll1.end();++pos)
{
cout<<*pos<<' ';
}
cout<<endl;
// insert 4 again and process return value
pair<IntSet::iterator,bool> status = coll1.insert(4);
if(status.second)
{
cout<<"4 inserted as element"
<< distance(coll1.begin(),status.first) +1
<<endl;
}
else
{
cout<<"4 already exists" <<endl;
}
// 产生一个新的set,容纳升序排列的ints,并以原本那个sets元素作为初值
set<int> coll2(coll1.begin(),coll1.end());
// print all elements of the copy
copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
cout<<endl;
// 移除数值为3的元素之前的所有元素
coll2.erase(coll2.begin(),coll2.find(3));
// 所有数值为5的元素都被移除
int num;
num = coll2.erase(5);
cout<<num<<" elements removed"<<endl;
// 打印所有的元素
copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
cout <<endl;
}
运行结果:

参考文章:
C++标准程序库

浙公网安备 33010602011771号