再谈set和multiset容器

来源:互联网 发布:gartner 云计算 2015 编辑:程序博客网 时间:2024/06/07 16:14

转载出处:http://blog.csdn.net/sunshinewave/article/details/8068181

1 set和multiset容器的能力
set 和multiset容器的内部结构通常由平衡二叉树(balancedbinary tree)来实现。当元素放入容器中时,会按照一定的排序法则自动排序,默认是按照less<>排序规则来排序。这种自动排序的特性加速了元素查找的过程,但是也带来了一个问题:不可以直接修改set或multiset容器中的元素值,因为这样做就可能违反了元素自动排序的规则。如果你希望修改一个元素的值,必须先删除原有的元素,再插入新的元素。

2 set和multiset容器的操作
Constructor and Destructor

[cpp] view plaincopy
  1. set c:创建一个空的set或multiset容器  
  2. set c(op):创建一个空的使用op作为排序法则的set或multiset容器  
  3. set c1(c2):创建一个已存在的set或multiset容器的复制品,容器的类型和所有元素一同复制  
  4. set c(beg, end):创建一个set或multiset容器,并且以[beg, end)范围中的元素进行初始化  
  5. set c(beg, end, op):创建一个使用op作为排序法则的set或multiset容器,并且以[beg, end)范围中的元素进行初始化  
  6. c.~set():容器的析构函数,销毁所有的元素,释放所有的分配内存  

上面的set可以是下面几种形式:

[cpp] view plaincopy
  1. set<type>:以less<>为排序法则的set  
  2. set<type, op>:以op为排序法则的set  
  3. multiset<type>:以less<>为排序法则的multiset  
  4. multiset<type, op>:以op为排序法则的multiset  

从上面我们可以看到,可以从两个地方来指定排序法则:

(1)作为模板参数

例如:std::set<int, greater<int> > col1;

这种情况下,排序法则本身作为容器类型的一部分。对于一个set或者multiset容器,只有当元素类型和排序法则类型都相同时,他们的类型才被认为相同,否则就是不同类型的容器。

(2)作为构造函数参数
例如:std::set<int> col1(greater<int>);

这种情况下指定的排序法则不作为容器类型的一部分,你可以为相同类型的容器指定不同的排序规则。这通常应用于要求相同的容器类型,但排序规则可以不同的场合。

Size and Comparing
set 
和multiset容器同样提供size(), empty(), max_size()三个关于查询元素数目的接口,提供==,!=, <, <=, >, >=等比较操作符。但值得注意的是比较操作符只针对相同类型的容器,元素类型和排序法则类型都必须相同。

Special Search Operations
set和multiset容器的内部结构对于元素的查找提供了优化空间,所以它们提供了一些特殊的查找接口,这些查找操作通常要比同名的通用算法高效,所以在相同的条件下应该优先使用这些接口。

[cpp] view plaincopy
  1. count(val): 返回容器中值等于val的元素数目。  
  2. find(val): 返回容器中值等于val的第一个元素的iterator位置;如果没有匹配元素,则返回end()位置。  
  3. lower_bound(val): 返回容器中第一个值大于或等于val的元素的iterator位置。  
  4. upper_bound(val): 返回容器中第一个值大于val的元素的iterator位置。  
  5. equal_range(val): 返回容器中值等于val的所有元素的范围[beg, end)组成的pair<beg, end> 。  

下面我们看一个使用lower_bound(), upper_bound和equal_range(val)例子,以加深对它们的理解:

[cpp] view plaincopy
  1. #include <iostream>  
  2. #include <set>  
  3. #include "print.hpp"  
  4. using namespace std;  
  5. int main()  
  6. {  
  7.     multiset<int> col1;  
  8.    
  9.     col1.insert(2);  
  10.     col1.insert(5);  
  11.     col1.insert(4);  
  12.     col1.insert(6);  
  13.     col1.insert(1);  
  14.     col1.insert(5);  
  15.    
  16.     PRINT_ELEMENTS(col1, "col1:");  
  17.     cout << endl;  
  18.    
  19.     multiset<int>::const_iteratorpos;  
  20.     pair<multiset<int>::iterator,multiset<int>::iterator> range;  
  21.    
  22.     cout << "lower_bound(3):" << *col1.lower_bound(3) << endl;  
  23.     cout << "upper_bound(3):" << *col1.upper_bound(3) << endl;  
  24.     range = col1.equal_range(3);  
  25.     cout << "equal_range(3):" << *range.first << " " << *range.second<< endl;  
  26.     cout << "elements withvalue(3): ";  
  27.     for (pos = range.first; pos !=range.second; ++pos)  
  28.     {  
  29.         cout << *pos<< " ";  
  30.     }  
  31.     cout << endl;  
  32.     cout << endl;  
  33.    
  34.     cout << "lower_bound(5):" << *col1.lower_bound(5) << endl;  
  35.     cout << "upper_bound(5):" << *col1.upper_bound(5) << endl;  
  36.     range = col1.equal_range(5);  
  37.     cout << "equal_range(5):" << *range.first << " " << *range.second<< endl;  
  38.     cout << "elements withvalue(5): ";  
  39.     for (pos = range.first; pos !=range.second; ++pos)  
  40.     {  
  41.         cout << *pos<< " ";  
  42.     }  
  43.     cout << endl;  
  44. }  
  45. 执行结果如下:  
  46. col1: 1 2 4 5 5 6   
  47.    
  48. lower_bound(3): 4  
  49. upper_bound(3): 4  
  50. equal_range(3): 4 4  
  51. elements with value(3):   
  52.    
  53. lower_bound(5): 5  
  54. upper_bound(5): 6  
  55. equal_range(5): 5 6  
  56. elements with value(5): 5 5   

Assignment
set
和multiset容器只提供最基本的赋值操作:

[cpp] view plaincopy
  1. c1 = c2: 把c2的所有元素复制到c1中,同时c1原有的元素被销毁。  
  2. c1.swap(c2): 交换c1和c2的元素。  
  3. swap(c1, c2): 同上,只不过这是一个通用算法。  

需要注意的是两个容器的类型要一致(包括元素类型和排序法则类型)。

Inserting and Removing Elements
set
和multiset容器的插入和删除元素接口跟其他容器也非常类似,但在细节上却存在差别。

[cpp] view plaincopy
  1. c.insert(elem): 在容器中插入元素elem的一份拷贝,并返回新元素的iterator位置;如果是set容器,同时还返回是否插入成功的标志。  
  2. c.insert(pos, elem): 在容器中插入元素elem的一份拷贝,并返回新元素的iterator位置;因为set和multiset容器的元素是自动排序的,所以pos位置只是插入位置的一                          个提 示,设置恰当的话,可以提高插入元素的效率。  
  3. c.insert(beg, end): 在容器中插入[beg, end)范围中所有元素的拷贝,没有返回值。  
  4. c.erase(val): 删除容器中所有值为val的元素,返回删除元素的数目。  
  5. c.erase(pos): 删除容器中位置pos处的元素,没有返回值。  
  6. c.erase(beg, end): 删除容器中[ben, end)范围内所有的元素,没有返回值。  
  7. c.clear(): 删除容器中所有元素,使容器成为空容器。  

其中我们重点说一下c.insert(elem)接口。

对于set容器,它的定义如下:

pair<iterator, bool> insert(const TYPE& val);

而对于multiset容器,它的定义如下:

iterator insert(const TYPE& val);

它 们的不同就是set容器的insert接口返回的是一个pair<iterator,bool>,而multiset容器的insert接口直接返回一个iterator。这是因为set容器中不允许有重复的元素,如果容器中已经存在一个跟插入值相同的元素,那么插入操作就会失败,而pair中的bool值就是标识插入是否成功的。而multiset不存在这个问题。

3 set和multiset容器的异常处理

因为set和multiset容器的独特内部结构,当发生异常时,也可以把影响减到最小。也就是说,跟list容器一样,set和multiset容器的操作要么成功,要么对原有容器没有影响。

运行时指定排序法则

通常情况下,我们是在定义容器时指定排序法则,就像下面形式:

std::set<int, greater<int> > col1;

或者

std::set<int> col1;    //use defaultsorting criterion less<>

 

然而,如果你需要在运行时动态指定容器的排序法则,或者你希望对于相同的容器类型却有着不同的排序法则,那么就要做一些特殊处理。下面我们看一个例子:

[cpp] view plaincopy
  1. #include <iostream>  
  2. #include <set>  
  3. #include "print.hpp"  
  4. using namespace std;  
  5.    
  6. template <typename T>  
  7. class RuntimeCmp   
  8. {  
  9.     public:  
  10.         enum cmp_mode {normal,reverse};  
  11.     private:  
  12.         cmp_mode mode;  
  13.     public:  
  14.         RuntimeCmp(cmp_mode m =normal) : mode(m) {}  
  15.    
  16.         bool operator() (constT& t1, const T& t2)  
  17.         {  
  18.             returnmode == normal ? t1 < t2 : t1 > t2;  
  19.         }  
  20.    
  21.         bool operator== (constT& rhv)   
  22.         {  
  23.             returnmode == rhv.mode;  
  24.         }  
  25. };  
  26.    
  27. typedef set<int, RuntimeCmp<int> > IntSet;  
  28.    
  29. //pre-declare  
  30. void fill(IntSet& col1);  
  31.    
  32. int main()  
  33. {  
  34.     IntSet col1;  
  35.     fill(col1);  
  36.     PRINT_ELEMENTS(col1, "col1:");  
  37.    
  38.     RuntimeCmp<int>reverse_cmp(RuntimeCmp<int>::reverse);  
  39.     IntSet col2(reverse_cmp);  
  40.     fill(col2);  
  41.     PRINT_ELEMENTS(col2, "col2:");  
  42.    
  43.     if (col1 == col2)   
  44.     {  
  45.         cout << "col1and col2 is equal" <<endl;  
  46.     }  
  47.     else  
  48.     {  
  49.         if (col1 <col2)   
  50.         {  
  51.             cout<< "col1 is less than col2" << endl;  
  52.         }  
  53.         else   
  54.         {  
  55.             cout<< "col1 is greater than col2" << endl;  
  56.         }  
  57.     }  
  58.     return 0;  
  59. }  
  60.    
  61. void fill(IntSet& col1)   
  62. {  
  63.     col1.insert(2);  
  64.     col1.insert(3);  
  65.     col1.insert(6);  
  66.     col1.insert(5);  
  67.     col1.insert(1);  
  68.     col1.insert(4);  
  69. }  
  70. 运行结果如下:  
  71. col1 1 2 3 4 5 6   
  72. col2 6 5 4 3 2 1   
  73. col1 is less than col2  

这里例子中,col1和col2有着相同的类型:set<int,RuntimeCmp<int> >,但是它们的排序法则却不相同,一个升序,一个降序。这都是通过自定义的函数对象来实现的,所以函数对象比普通函数有着更加灵活与强大的控制,可以满足一些特殊的需求。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 高血压200降不下去.怎么办 高血压吃药降不下来怎么办 合肥房子卖了户口怎么办 吃了粽子胃难受怎么办 突然血压高怎么办需要吃药吗? 胃一阵阵疼然后拉肚子怎么办 橱子和墙壁不平怎么办 复印选项是英文不认识怎么办 防盗门锁与门框结合不好怎么办 仿瓷涂料墙壁脏了怎么办 油笔画到墙纸上怎么办 水笔画在墙纸上怎么办 屋里有股石灰味怎么办 厨房太阳对着晒怎么办 房子有太阳西斜怎么办 房子晒到太阳很热怎么办 房子被太阳热了怎么办 房间西晒窗帘不遮光怎么办 新建房屋一面墙体有裂缝怎么办 卫生间地砖缝隙出现渗水怎么办 西户窗户太晒怎么办 西晒的墙面很烫怎么办 儿童房颜色太粉了怎么办? 小孩在家里偷钱怎么办 脾气不好的猫该怎么办 二年孩子偷钱怎么办 孩子偷钱2000报警怎么办? 我儿子十岁老是偷钱怎么办 13孩子偷同学钱怎么办 孩子偷同学的钱怎么办 儿子十四岁了老偷钱怎么办 发现初中生的儿子偷钱怎么办 被亲戚怀疑儿子偷钱怎么办 房门选的太白了怎么办 大厅地砖颜色比墙砖浅怎么办 房屋外墙渗水物业不管怎么办 走丢了怎么办教学反思 托班教案迷路了怎么办 大班安全教案遇到小偷怎么办 小班孩子舞台表演找不到位置怎么办 懂你英语学完了怎么办