注:博客内容均来自于对《C++标准库》侯捷,华中科技大学出版社一书的笔记。转载请注明出处。
所有例程在Red Hat Linux 3.2.2-5版本上编译运行,g++的版本是 g++ (GCC) 3.2.2 20030222。
1、set和multiset
set和multiset里面的元素都是“有序的”。因为set和multiset会根据特定的排序准则,自动将元素排序。不同的是set容器里面不允许有重复的元素,multiset容器允许存在重复的元素。实际上,set和multiset通常以红黑树(red-black tree)实现的。红黑树在改变元素数量和元素搜索方面表现都很出色,它保证节点安插时最多只会作两个重新连接(relink)动作,而且到达某一元素的最长路径深度,最多只是最短路径的两倍。
自动排序的主要优点在于使二叉树查找元素时具有良好的性能。其查找算法具有对数复杂度!
但是,自动排序造成set和multiset的一个重要限制:不能直接改变元素值,因为这样会打乱原本的正确顺序,因此,要改变元素值,必须先删除旧元素,在插入新元素,因此:
1:set和multiset一样,不提供用来直接存取元素的任何操作函数;
2:通过迭代器进行元素的间接存取,有一个限制:元素值是常数。
2、set和multiset的操作函数
2.1 创建、构造和析构
1> 以template参数定义之。
std::set<int, std::geater<int> > col1;
第二个参数排序准则的型别,实际的排序准则是容器所产生的仿函数。下面一个例子来说明这种方式。
example:
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
class Person
{
public:
Person(string a,string b) :
strFirstname(a),strLastname(b)
{}
public:
string firstname() const
{
return strFirstname;
}
string lastname() const
{
return strLastname;
}
private:
const string strFirstname;
const string strLastname;
};
//仿函数实现自定义排序
class PersonSortCriterion
{
public :
bool operator()(const Person &p1, const Person &p2)
{
return (p1.lastname() < p2.lastname() ||
(!(p2.lastname() < p1.lastname()) &&
p1.firstname() < p2.firstname()));
}
};
int main(int argc, char *argv[])
{
typedef set<Person, PersonSortCriterion> PersonSet;
PersonSet col1;
//创建元素,并添加到容器
Person p1("Jay","Chou");
Person p2("Robin","Chou");
Person p3("Robin","Lee");
Person p4("Bob","Smith");
col1.insert(p1);
col1.insert(p2);
col1.insert(p3);
col1.insert(p4);
PersonSet::iterator pos;
//输出PersonSet中的所有元素
for(pos = col1.begin(); pos != col1.end(); ++pos)
{
cout<<pos->firstname()<< " " << pos->lastname() << endl;
}
cout<<endl;
return 0;
}
2> 以构造函数定义之。
这种情况下,同一个型别可以运行不同的排序准则,而排序的初始值或者状态也可以不同。
如果执行期间才获得排序准则,而且需要用到不同的排序准则,那么使用这种方式再好不过了!
example:
#include <iostream>
#include <set>
#include "print.h"
using namespace std;
template <class T>
class RuntimeCmp
{
public:
enum cmp_mode {normal, reverse};
private:
cmp_mode mode;
public:
RuntimeCmp(cmp_mode m = normal): mode(m)
{
}
bool operator() (const T& t1, const T& t2) const
{
return mode == normal ? t1<t2 : t2<t1;
}
bool operator== (const RuntimeCmp& rc)
{
return mode == rc.mode;
}
};
typedef set<int, RuntimeCmp<int> > IntSet;
void fill(IntSet& set)
{
set.insert(4);
set.insert(7);
set.insert(5);
set.insert(1);
set.insert(6);
set.insert(2);
set.insert(5);
}
int main(int argc, char *argv[])
{
IntSet col1;
fill(col1);
PRINT_ELEMENTS(col1, "col1: ");
RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);
IntSet col2(reverse_order);
fill(col2);
PRINT_ELEMENTS(col2, "col2: ");
col1= col2;
col1.insert(3);
PRINT_ELEMENTS(col1, "col1: ");
if(col1.value_comp() == col2.value_comp())
{
cout<<"col1 == col2"<<endl;
}
else
{
cout<<"col1 != col2"<<endl;
}
return 0;
}
注:比较操作只能比较类型相同的容器。不同类型的容器比较会出错!!
3、例子
#include <iostream>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
int main(int argc, char *argv[])
{
typedef set<int, greater<int> > IntSet;
IntSet col1;
col1.insert(4);
col1.insert(3);
col1.insert(5);
col1.insert(1);
col1.insert(6);
col1.insert(2);
col1.insert(5);
IntSet::iterator pos;
for(pos = col1.begin(); pos != col1.end(); ++pos)
{
cout<< *pos << " ";
}
cout<< endl;
pair<IntSet::iterator, bool> status = col1.insert(4);
if(status.second)
{
cout<< "4 inset as a element "
<< distance(col1.begin(), status.first) + 1
<< endl;
}
else
cout<< "4 already exists "<<endl;
set<int> col2(col1.begin(),
col1.end());
copy(col2.begin(),col2.end(),
ostream_iterator<int>(cout," "));
cout<<endl;
col2.erase(col2.begin(), col2.find(3));
int num;
num = col2.erase(5);
cout<<num<<"element(s) removed"<<endl;
copy(col2.begin(),col2.end(),
ostream_iterator<int>(cout," "));
cout<<endl;
return 0;
}
运行结果