set和mutilset会根据特定的排序准则,自动将元素排序。两者不同之处在于mutilset允许元素重复而set不允许。属于关联式容器。
#include <set>
只要是可以根据某种排序准则作比较的任意类型T都可以成为set或mutilset的元素。
所谓的排序准则,意义如下:
(1) 必须是非对称的
对operator< 而言, 如果 x<y 为true, 则 y<x 为false
对判断式 op()而言, 如果 op(x, y) 为true, 则 op(y, x) 为false
(2) 必须是可传递的
对 operator 而言, 如果 x<y 为true, 且 y<z 为true, 则 x<z 为true
对 op() 而言, 如果 op(x, y) 为true且op(y, z)为true, 则 op(x, z)为true
(3) 必须是非自反的
对 operator< 而言, x<x 永远为false
对 op() 而言, op(x, x) 永远为false
(4) 必须有等效传递性:如果 a=b, b=c 成立, 则 a=c 必然成立
这意味着必须区分less和equal, 像 operator <= 这样的准则不符合条件。
根据这些性质,排序准则也被用来检查等效性。也就是说,两个元素,如果没有任何一个小于另一个,则视为重复。
mutilset等效元素的次序是随机但稳定的。
特性
set和mutilset通常以平衡二叉树实现。
自动排序的优点在于令二叉树查找元素时拥有良好的效能,查找函数具有对数复杂度。
自动排序的限制是,不能直接改变元素值,因为这样会打乱原本的顺序。
要改变元素,必须先删除旧元素,再插入新元素
-> set和mutilset不提供任何操作函数可以直接访问元素
-> 通过迭代器进行元素见访问,从迭代器的角度看,元素值是常量
部分操作
c.key_comp() | 返回比较准则 |
c.value_comp() | 返回针对value的比较准则 |
c.empty() | 是否为空 |
c.size() | 当前元素个数 |
c.max_size() | |
c1 == c2 | |
c1 != c2 | |
c1 < c2 | |
c1 > c2 | |
c1 <= c2 | |
c1 >= c2 | |
c.count(val) | 返回元素值为value的元素个数 |
c.find(val) | 返回元素值为value的第一个元素,如果找不到就返回end |
c.lower_bound(val) | 返回val的第一个可安插位置,也就是 元素值>=val 的第一个位置 |
c.upper_bound(val) | 返回val的最后一个可安插位置,也就是 元素值>=val 的第一个位置 |
c.equal_range(val) | 返回val可被安插的第一个位置和最后一个位置,也就是 元素值==val 的元素区间 |
c = c2 | |
c = rv | |
c = initlist | |
c1.swap(c2) | |
swap(c1,c2) | |
c.begin() | |
c.end() | |
c.cbegin() | |
c.cend() | |
c.rbegin() | |
c.rend() | |
c.crbegin() | |
c.crend() | |
c.insert(val) | 安插一个val拷贝,返回新元素位置, 不论是否成功 |
c.insert(pos,val) | 安插一个val拷贝,返回新元素位置(pos是个提示,可加快速度) |
c.insert(beg,end) | 将区间[beg,end)内所有元素的拷贝安插到c,五返回值 |
c.insert(initlist) | |
c.emplace(args...) | |
c.emplace_hint(pos,args...) | |
c.erase(val) | 移除与val之相等的所有元素,返回被移除元素的个数 |
c.erase(pos) | 移除pos位置上的元素,无返回值 |
c.erase(beg,end) | 移除区间内的所有元素,无返回值 |
c.clear() | 清空 |
lower_bound(), upper_bound()和equal_range()的使用例子
#include <iostream>
#include <set>
using namespace std;
int main ()
{
set<int> c;
c.insert(1);
c.insert(2);
c.insert(4);
c.insert(5);
c.insert(6);
cout << "lower_bound(3): " << *c.lower_bound(3) << endl;
cout << "upper_bound(3): " << *c.upper_bound(3) << endl;
cout << "equal_range(3): " << *c.equal_range(3).first << " " << *c.equal_range(3).second << endl;
cout << endl;
cout << "lower_bound(5): " << *c.lower_bound(5) << endl;
cout << "upper_bound(5): " << *c.upper_bound(5) << endl;
cout << "equal_range(5): " << *c.equal_range(5).first << " " << *c.equal_range(5).second << endl;
}
--------------------------------------
lower_bound(3): 4
upper_bound(3): 4
equal_range(3): 4 4
lower_bound(5): 5
upper_bound(5): 6
equal_range(5): 5 6
使用示例
#include <iostream>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
// type of the collection:
// - no duplicates
// - elements are integral values
// - descending order
set<int,greater<int>> coll1;
// insert elements in random order using different member functions
coll1.insert({4,3,5,1,6,2});
coll1.insert(5);
// print all elements
for (int elem : coll1) {
cout << elem << ’ ’;
}
cout << endl;
// insert 4 again and process return value
auto 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;
}
// assign elements to another set with ascending order
set<int> coll2(coll1.cbegin(),coll1.cend());
// print all elements of the copy using stream iterators
copy (coll2.cbegin(), coll2.cend(),
ostream_iterator<int>(cout," "));
cout << endl;
// remove all elements up to element with value 3
coll2.erase (coll2.begin(), coll2.find(3));
// remove all elements with value 5
int num;
num = coll2.erase (5);
cout << num << " element(s) removed" << endl;
// print all elements
copy (coll2.cbegin(), coll2.cend(),
ostream_iterator<int>(cout," "));
cout << endl;
}
运行期指定准则
无论是将排序准则作为第二个template实参传入,或是采用默认的排序准则 less<>, 通常都会将排序准则定义为类型的一部分。但有时候必须在运行期处理排序准则,或者有时候需要对同一种数据类型采用不同的排序准则,此时就需要一个用来表示准则的特殊类型,能够在运行期才给定某个准则,以下程序说明了这种做法:
#include <iostream>
#include <set>
#include "print.hpp"
using namespace std;
// type for runtime sorting criterion
class RuntimeCmp {
public:
enum cmp_mode {normal, reverse};
private:
cmp_mode mode;
public:
// constructor for sorting criterion
// - default criterion uses value normal
RuntimeCmp (cmp_mode m=normal) : mode(m) {
}
// comparison of elements
// - member function for any element type
template <typename T>
bool operator() (const T& t1, const T& t2) const {
return mode==normal ? t1<t2 : t2<t1;
}
// comparison of sorting criteria
bool operator== (const RuntimeCmp& rc) const {
return mode == rc.mode;
}
};
// type of a set that uses this sorting criterion
typedef set<int,RuntimeCmp> IntSet;
int main()
{
// create, fill, and print set with normal element order
// - uses default sorting criterion
IntSet coll1 = { 4, 7, 5, 1, 6, 2, 5 };
PRINT_ELEMENTS (coll1, "coll1: ");
// create sorting criterion with reverse element order
RuntimeCmp reverse_order(RuntimeCmp::reverse);
// create, fill, and print set with reverse element order
IntSet coll2(reverse_order);
coll2 = { 4, 7, 5, 1, 6, 2, 5 };
PRINT_ELEMENTS (coll2, "coll2: ");
// assign elements AND sorting criterion
coll1 = coll2;
coll1.insert(3);
PRINT_ELEMENTS (coll1, "coll1: ");
// just to make sure...
if (coll1.value_comp() == coll2.value_comp()) {
cout << "coll1 and coll2 have the same sorting criterion" << endl;
}
else {
cout << "coll1 and coll2 have a different sorting criterion" << endl;
}
}
在这个程序中,class RuntimeCmp提供了一种泛化能力:允许在运行期间对任何数据类型指定排序准则。其构造函数设定以升序进行排序,用的是默认值normal。他也允许传递RuntimeCmp::reverse作为构造函数实参,实现降序排序。