set和multiset 会根据特定的排序准则,自动将元素排序。两者不同之处,在于multiset允许元素重复,而set不允许重复,如图1所示。
图1 set和multiset
在使用set和multiset之前,需要包含头头文件
namespace std
{
template < class T,
class Compare = less,
class Allocator = allcator >
class set;
template ,
class Allocator = allcator >
class multiset;
}
我们需要着重关注的是第二个模板参数,如果我们没有传入特殊的排序准则,就采用默认的less排序方式。Less是仿函数,采用operator<的比较方式。
和所有标准关联模式类似,set和multiset内部数据结构通常是以红黑树(RBT,平衡二叉树)构成,红黑树在改变元素和搜索元素具有良好的性能。比如在搜索元素时,set的搜寻成员函数算法具有对数复杂度,速度相对较快。其内部结构如图2。
图2 内部数据结构
Set的默认排序准则是less函数,我们也可以定义其他的排序准则,有以下两种排序定义方式:
1.template参数定义之:
std::set
这种情况下,排序准则是是型别的一部分,这是排序准则的通常指定用法,如果排序准则不相同,则不能相互比较。
2.以构造函数参数定义之:
这种情况下,同一个型别可以运用不同的排序准则,而排序准则的初始值或状态可以不同。如果执行期间才获得排序准则,而且需要用到不同的排序准则,以构造函数参数定义的方式则派上用场了。代码实现见实例三。
和vector、deque、list相比,set主要不同之处在于插入元素时具有自动排序功能的,其余的操作函数基本都相同,此次只列举set的特殊函数,代码实现见实例一。
set和multiset的搜寻操作函数count(elem) 返回“元素值为elem”的元素个数 find(elem) 返回“元素值为elem”的第一个元素,如果找不到就返回end() lower_bound(elem) 返回elem的第一个可安插位置,也就是“元素值>=elem”的第一个位置 upper_bound(elem)) 返回elem的最后一个可安插位置,也就是“元素值>elem”的第一个位置 equal_range(elem) 返回elem可安插的第一个位置和最后一个位置,即equal_range()将lower_bound()和upper_bound()的返回值做个一个pair返回。
Set的intsert返回值:
pair
insert(const vaule_type& elem);
iterator insert(iterator pos_hint, const value_type& elem);//参数pos_hint示位置提示
multiset的intsert返回值:
iterator insert(const vaule_type& elem);
iterator insert(iterator pos_hint, const value_type& elem);//;//参数pos_hint示位置提示
由于Multiset允许元素重复,而set不允许重复。如果将某元素插入一个set中,如果set中已经包含同值元素,则安插操作将失败,所以set的返回值型别可以是pari结构,返回值含义如下:
1.pair结构中的second成员表示安插是否成功。
2.pair结构中的first成员返回新元素的位置,或现存同值元素的位置。
set c;
...
if(c.insert(3.3).second)
{
std::cout << "3.3 inserted" << endl;
}
else
{
std::cout << "3.3 already exists << endl";
}
带有位置提示参数的安插函数,其返回值型别都一样,不论是set还是multiset。位置提示参数仅是可能给容器插入带来性能的提升,并不是指定插入的位置。
带有位置提示参数的安插函数是各种容器中的通用接口,事实上通用型安插迭代器(general inserters)就是通过这个接口实现的。
set或multiset的搜寻函数举例
void SetExample1()
{
cout << "*********SetExample1*********" << endl;
set 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_bound(3): " << *c.equal_range(3).first
<< " " << *c.equal_range(3).second << endl;
cout << "lower_bound(5): " << *c.lower_bound(5) << endl;
cout << "upper_bound(5): " << *c.upper_bound(5) << endl;
cout << "equal_bound(5): " << *c.equal_range(5).first
<< " " << *c.equal_range(5).second << endl;
}
set的综合应用
void SetExample2()
{
cout << "*********SetExample2*********" << endl;
typedef set >IntSet;
IntSet IntSetContainer;
IntSetContainer.insert(4);
IntSetContainer.insert(3);
IntSetContainer.insert(5);
IntSetContainer.insert(1);
IntSetContainer.insert(6);
IntSetContainer.insert(2);
IntSetContainer.insert(5);
IntSet::iterator pos;
for (pos = IntSetContainer.begin(); pos != IntSetContainer.end(); ++pos)
{
cout << *pos << " ";
}
cout << endl;
pair status = IntSetContainer.insert(4);
if (status.second)
{
cout << "4 insertend as element " << distance(IntSetContainer.begin(), status.first) + 1 < nSetContainLess(IntSetContainer.begin(), IntSetContainer.end());
copy(nSetContainLess.begin(), nSetContainLess.end(), ostream_iterator(cout , " "));
cout << endl;
//remove all elements from begin pos to element with value 3
nSetContainLess.erase(nSetContainLess.begin(), nSetContainLess.find(3));
//remove all elements with value 5;
int num;
num = nSetContainLess.erase(5);
cout << num << "element(s) removed" << endl;
//print all elemts
copy(nSetContainLess.begin(), nSetContainLess.end(), ostream_iterator(cout, " "));
cout << endl;
}
执行期指定排序准则
实现原理:
根据排序准则insert函数会在某个位置插入新元素,新插入的新元素会保证树的平衡状态不被改变,具体的排序准则均是通过运算符重载实现的。比如less仿函数调用operator(),透过"<"实现,great仿函数调用operator(),透过">"。执行期实现指定排序准则则是根据不同的参数调用不同操作符号来实现。
template
struct greater: public binary_function<_Ty, _Ty, bool>
{
// functor for operator>
bool operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator> to operands
return (_Left > _Right);
}
};
template
struct less: public binary_function<_Ty, _Ty, bool>
{
// functor for operator<
bool operator()(const _Ty& _Left, const _Ty& _Right) const
{
// apply operator< to operands
return (_Left < _Right);
}
};
实现源代码
template
class RuntimeCmp
{
public:
enum cmp_mode {normal, reverse};
private:
cmp_mode emMode;
public:
RuntimeCmp(cmp_mode m = normal) : emMode(m){
if (m== normal)
{
cout << "normal" << endl;
}
else
{
cout << "reverse" << endl;
}
}
bool operator() (const T &t1, const T &t2)const
{
return emMode == normal ? t1 < t2 :t2 < t1;//根据模式值确定需要调用的值
}
bool operator== (const RuntimeCmp& rc)
{
return emMode == rc.emMode;
}
};
typedef set >IntSet;
void fill(IntSet & nSet)
{
nSet.insert(4);
nSet.insert(7);
nSet.insert(5);
nSet.insert(1);
nSet.insert(6);
nSet.insert(2);
nSet.insert(5);
}
void SetExample3()
{
cout << "*********SetExample3*********" << endl;
IntSet nSetContainer;
fill(nSetContainer);
IntSet::iterator it = nSetContainer.begin();
PrintElements(nSetContainer, "Set1: ");
RuntimeCmp reverse_order(RuntimeCmp::reverse);
IntSet nSetContainer2(reverse_order);
fill(nSetContainer2);
PrintElements(nSetContainer2, "Set2: ");
nSetContainer = nSetContainer2;//不见赋值了元素,也赋值了排序准则
nSetContainer.insert(3);
PrintElements(nSetContainer, "Set1: ");
if (nSetContainer.value_comp() == nSetContainer2.value_comp())
{
cout << "set1 and set2 have same sorting criterion" << endl;
}
else
{
cout << "set1 and set2 have different sorting criterion" << endl;
}
}
图3 运行结果