STL之set和multiset详解
2016-05-15 19:22
471 查看
set和multiset 会根据特定的排序准则,自动将元素排序。两者不同之处,在于multiset允许元素重复,而set不允许重复,如图1所示。
图1 set和multiset
在使用set和multiset之前,需要包含头头文件<set>,Set 和multiset的原型如下:
我们需要着重关注的是第二个模板参数,如果我们没有传入特殊的排序准则,就采用默认的less排序方式。Less是仿函数,采用operator<的比较方式。
图2 内部数据结构
1.template参数定义之:
std::set<int,std::greate<int> > coll;
这种情况下,排序准则是是型别的一部分,这是排序准则的通常指定用法,如果排序准则不相同,则不能相互比较。
2.以构造函数参数定义之:
这种情况下,同一个型别可以运用不同的排序准则,而排序准则的初始值或状态可以不同。如果执行期间才获得排序准则,而且需要用到不同的排序准则,以构造函数参数定义的方式则派上用场了。代码实现见实例三。
Set的intsert返回值:
pair<iterator, bool> 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还是multiset。位置提示参数仅是可能给容器插入带来性能的提升,并不是指定插入的位置。
带有位置提示参数的安插函数是各种容器中的通用接口,事实上通用型安插迭代器(general inserters)就是通过这个接口实现的。
实现原理:
根据排序准则insert函数会在某个位置插入新元素,新插入的新元素会保证树的平衡状态不被改变,具体的排序准则均是通过运算符重载实现的。比如less仿函数调用operator(),透过"<"实现,great仿函数调用operator(),透过">"。执行期实现指定排序准则则是根据不同的参数调用不同操作符号来实现。
实现源代码
图3 运行结果
图1 set和multiset
在使用set和multiset之前,需要包含头头文件<set>,Set 和multiset的原型如下:
namespace std { template < class T, class Compare = less<T>, class Allocator = allcator<t> > class set; template <class T, class Compare = less<T>, class Allocator = allcator<t> > class multiset; }
我们需要着重关注的是第二个模板参数,如果我们没有传入特殊的排序准则,就采用默认的less排序方式。Less是仿函数,采用operator<的比较方式。
Set 和multiset数据结构
和所有标准关联模式类似,set和multiset内部数据结构通常是以红黑树(RBT,平衡二叉树)构成,红黑树在改变元素和搜索元素具有良好的性能。比如在搜索元素时,set的搜寻成员函数算法具有对数复杂度,速度相对较快。其内部结构如图2。图2 内部数据结构
Set的排序准则
Set的默认排序准则是less函数,我们也可以定义其他的排序准则,有以下两种排序定义方式:1.template参数定义之:
std::set<int,std::greate<int> > coll;
这种情况下,排序准则是是型别的一部分,这是排序准则的通常指定用法,如果排序准则不相同,则不能相互比较。
2.以构造函数参数定义之:
这种情况下,同一个型别可以运用不同的排序准则,而排序准则的初始值或状态可以不同。如果执行期间才获得排序准则,而且需要用到不同的排序准则,以构造函数参数定义的方式则派上用场了。代码实现见实例三。
Set和multiset操作函数
和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返回。 |
pair<iterator, bool> 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<double> 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<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_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<int, greater<int> >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<IntSet::iterator, bool> status = IntSetContainer.insert(4); if (status.second) { cout << "4 insertend as element " << distance(IntSetContainer.begin(), status.first) + 1 <<endl; } else { cout << "4 already exists!" << endl; } //assign element to another set with ascending order set<int> nSetContainLess(IntSetContainer.begin(), IntSetContainer.end()); copy(nSetContainLess.begin(), nSetContainLess.end(), ostream_iterator<int>(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<int>(cout, " ")); cout << endl; }
示例三
执行期指定排序准则实现原理:
根据排序准则insert函数会在某个位置插入新元素,新插入的新元素会保证树的平衡状态不被改变,具体的排序准则均是通过运算符重载实现的。比如less仿函数调用operator(),透过"<"实现,great仿函数调用operator(),透过">"。执行期实现指定排序准则则是根据不同的参数调用不同操作符号来实现。
template<class _Ty> 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<class _Ty> 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 T> 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<int, RuntimeCmp<int> >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<int> reverse_order(RuntimeCmp<int>::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 运行结果
相关文章推荐
- set 命令特殊用法
- ORACLE SQL-UPDATE、DELETE、INSERT优化和使用技巧分享
- You must SET PASSWORD before executing this statement的解决方法
- 让你的insert操作速度增加1000倍的方法
- 批处理 Set 命令详解 让你理解set命令第1/2页
- SQL Server中的XML数据进行insert、update、delete
- ASP中set与dim的区别(自己的理解)
- SQL Server中的XML数据进行insert、update、delete操作实现代码
- 正确使用MySQL INSERT INTO语句
- mysql中insert与select的嵌套使用方法
- 深入C#中get与set的详解
- 浅析STL中的常用算法
- STL区间成员函数及区间算法总结
- sql Set IDENTITY_INSERT的用法
- 将表里的数据批量生成INSERT语句的存储过程 增强版
- MySQL中insert语句的使用与优化教程
- 详解MySQL数据库insert和update语句
- set_include_path在win和linux下的区别
- Insert Date and Time into Access
- 数据库插入数据之select into from与insert into select区别详解