一、set和multiset基础

set和multiset会根据特定的排序准则,自动将元素进行排序。不同的是后者允许元素重复而前者不允许。


需要包含头文件:

#include <set>

set和multiset都是定义在std空间里的类模板:





[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. template<class _Kty,  
  2.     class _Pr = less<_Kty>,  
  3.     class _Alloc = allocator<_Kty> >  
  4. class set  




[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. template<class _Kty,  
  2.     class _Pr = less<_Kty>,  
  3.     class _Alloc = allocator<_Kty> >  
  4. class multiset  


只要是可复赋值、可拷贝、可以根据某个排序准则进行比较的型别都可以成为它们的元素。第二个参数用来定义排序准则。缺省准则less是一个仿函数,以operator<对元素进行比较。


所谓排序准则,必须定义strict weak ordering,其意义如下:

1、必须使反对称的。

对operator<而言,如果x<y为真,则y<x为假。

2、必须使可传递的。

对operator<而言,如果x<y为真,且y<z为真,则x<z为真。

3、必须是非自反的。

对operator<而言,x<x永远为假。

因为上面的这些特性,排序准则可以用于相等性检验,就是说,如果两个元素都不小于对方,则它们相等。

二、set和multiset的功能

和所有关联式容器类似,通常使用平衡二叉树完成。事实上,set和multiset通常以红黑树实作而成。

自动排序的优点是使得搜寻元素时具有良好的性能,具有对数时间复杂度。但是造成的一个缺点就是:

不能直接改变元素值。因为这样会打乱原有的顺序。

改变元素值的方法是:先删除旧元素,再插入新元素。

存取元素只能通过迭代器,从迭代器的角度看,元素值是常数。

三、操作函数

构造函数和析构函数

set的形式可以是:

有两种方式可以定义排序准则:

1、以template参数定义:





[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. set<int,greater<int>> col1;  

此时,排序准则就是型别的一部分。型别系统确保只有排序准则相同的容器才能被合并。


程序实例:





[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. #include <iostream>  
  2. #include <set>  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     set<int> s1;  
  8.     set<int,greater<int> > s2;  
  9.   
  10.     for (int i = 1;i < 6;++i)  
  11.     {  
  12.         s1.insert(i);  
  13.         s2.insert(i);  
  14.     }  
  15.     if(s1 == s2)  
  16.         cout << "c1 equals c2 !" << endl;  
  17.     else  
  18.         cout << "c1 not equals c2 !" << endl;  
  19. }  

程序运行会报错。但是如果把s1的排序准则也指定为greater<int>便运行成功。



2、以构造函数参数定义。

这种情况下,同一个型别可以运用不同的排序准则,而排序准则的初始值或状态也可以不同。如果执行期才获得排序准则,而且需要用到不同的排序准则,这种方式可以派上用场。

程序实例:





[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. #include <iostream>  
  2. #include "print.hpp"  
  3. #include <set>  
  4. using namespace std;  
  5.   
  6. template <class T>  
  7. class RuntimeCmp{  
  8. public:  
  9.     enum cmp_mode{normal,reverse};  
  10. private:  
  11.     cmp_mode mode;  
  12. public:  
  13.     RuntimeCmp(cmp_mode m = normal):mode(m){}  
  14.   
  15.     bool operator()(const T &t1,const T &t2)  
  16.     {  
  17.         return mode == normal ? t1 < t2 : t2 < t1;  
  18.     }  
  19.   
  20.     bool operator==(const RuntimeCmp &rc)  
  21.     {  
  22.         return mode == rc.mode;  
  23.     }  
  24. };  
  25.   
  26. typedef set<int,RuntimeCmp<int> > IntSet;  
  27.   
  28. void fill(IntSet& set);  
  29.   
  30. int main()  
  31. {  
  32.     IntSet set1;  
  33.     fill(set1);  
  34.     PRINT_ELEMENTS(set1,"set1:");  
  35.   
  36.     RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);  
  37.   
  38.     IntSet set2(reverse_order);  
  39.     fill(set2);  
  40.     PRINT_ELEMENTS(set2,"set2:");  
  41.   
  42.     set1 = set2;//assignment:OK  
  43.     set1.insert(3);  
  44.     PRINT_ELEMENTS(set1,"set1:");  
  45.   
  46.     if(set1.value_comp() == set2.value_comp())//value_comp <span style="font-family: verdana, arial, helvetica, sans-serif; ">Returns the comparison object associated with the container</span>  
  47.         cout << "set1 and set2 have the same sorting criterion" << endl;  
  48.     else  
  49.         cout << "set1 and set2 have the different sorting criterion" << endl;  
  50. }  
  51.   
  52. void fill(IntSet &set)  
  53. {  
  54.     set.insert(4);  
  55.     set.insert(7);  
  56.     set.insert(5);  
  57.     set.insert(1);  
  58.     set.insert(6);  
  59.     set.insert(2);  
  60.     set.insert(5);  
  61. }  

运行结果:





虽然set1和set2的而比较准则本身不同,但是型别相同,所以可以进行赋值操作。


非变动性操作

注意:元素比较操作只能用于型别相同的容器。

特殊的搜寻函数

赋值

赋值操作两端的容器必须具有相同的型别,但是比较准则本身可以不同,但是其型别必须相同。如果比较准则的不同,准则本身也会被赋值或交换。

迭代器相关函数

元素的插入和删除

注意:插入函数的返回值不完全相同。

set提供的插入函数:





[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. pair<iterator,bool> insert(const value_type& elem);   
  2. iterator  insert(iterator pos_hint, const value_type& elem);   

multiset提供的插入函数:






[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. iterator  insert(const value_type& elem);   
  2. iterator  insert(iterator pos_hint, const value_type& elem);  

返回值型别不同的原因是set不允许元素重复,而multiset允许。当插入的元素在set中已经包含有同样值的元素时,插入就会失败。所以set的返回值型别是由pair组织起来的两个值:


第一个元素返回新元素的位置,或返回现存的同值元素的位置。第二个元素表示插入是否成功。

set的第二个insert函数,如果插入失败,就只返回重复元素的位置!


但是,所有拥有位置提示参数的插入函数的返回值型别是相同的。这样就确保了至少有了一个通用型的插入函数,在各种容器中有共通接口。

注意:还有一个返回值不同的情况是:作用于序列式容器和关联式容器的erase()函数:

序列式容器的erase()函数:





[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. iterator erase(iterator pos);   
  2. iterator erase(iterator beg, iterator end);  

关联式容器的erase()函数:






[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. void     erase(iterator pos);   
  2. void     erase(iterator beg, iterator end);   

这完全是为了性能的考虑。因为关联式容器都是由二叉树实现,搜寻某元素并返回后继元素可能很费时。


五、set应用示例:






[cpp] ​​view plain​ ​ ​​copy​


 ​​print​​​​?​

  1. #include <iostream>  
  2. #include <set>  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     typedef set<int,greater<int> > IntSet;  
  8.     IntSet s1;  
  9.   
  10.     s1.insert(4);  
  11.     s1.insert(3);  
  12.     s1.insert(5);  
  13.     s1.insert(1);  
  14.     s1.insert(6);  
  15.     s1.insert(2);  
  16.     s1.insert(5);  
  17.     //the inserted element that has the same value with a element existed is emitted  
  18.   
  19.     copy(s1.begin(),s1.end(),ostream_iterator<int>(cout," "));  
  20.     cout << endl << endl;  
  21.   
  22.     pair<IntSet::iterator,bool> status = s1.insert(4);  
  23.     if(status.second)  
  24.         cout << "4 is inserted as element "  
  25.         << distance(s1.begin(),status.first) + 1 << endl;  
  26.     else  
  27.         cout << "4 already exists in s1" << endl;  
  28.     copy(s1.begin(),s1.end(),ostream_iterator<int>(cout," "));  
  29.     cout << endl << endl;  
  30.   
  31.     set<int>  s2(s1.begin(),s1.end());//default sort criterion is less<  
  32.     copy(s2.begin(),s2.end(),ostream_iterator<int>(cout," "));  
  33.     cout << endl << endl;  
  34. }  


上述程序最后新产生一个set:s2,默认排序准则是less。以s1的元素作为初值。


注意:s1和s2有不同的排序准则,所以他们的型别不同,不能直接进行相互赋值或比较。

运行结果:







Defined in header ​​<iterator>​


 

 

 

(1)

 





template< class C > 

auto rbegin( C& c ) -> decltype(c.rbegin());

(since C++14) 

(until C++17)





template< class C > 

constexpr auto rbegin( C& c ) -> decltype(c.rbegin());

(since C++17)





 

(1)

 




template< class C > 

auto rbegin( const C& c ) -> decltype(c.rbegin());

(since C++14) 

(until C++17)




template< class C > 

constexpr auto rbegin( const C& c ) -> decltype(c.rbegin());

(since C++17)




 

(2)

 



template< class T, size_t N > 

reverse_iterator<T*> rbegin( T (&array)[N] );

(since C++14) 

(until C++17)



template< class T, size_t N > 

constexpr reverse_iterator<T*> rbegin( T (&array)[N] );

(since C++17)



 

(3)

 


template< class C > 

auto crbegin( const C& c ) -> decltype(std::rbegin(c));

(since C++14) 

(until C++17)


template< class C > 

constexpr auto crbegin( const C& c ) -> decltype(std::rbegin(c));

(since C++17)


 

 

 

















































































































































































































































































































































































































































Returns an iterator to the reverse-beginning of the given container ​​c​​ or array ​​array​​.


1) Returns a possibly const-qualified iterator to the reverse-beginning of the container ​​c​​.


2) Returns ​​std::reverse_iterator​​<T*> to the reverse-beginning of the array ​ ​array​​.


3) Returns a const-qualified iterator to the reverse-beginning of the container ​​c​​.



【C/C++开发】容器set和multiset,C++11对vector成员函数的扩展(cbegin()、cend()、crbegin()、crend()、emplace()、data())_#include


Parameters

c

-

a container with a ​​rbegin​​ method

array

-

an array of arbitrary type

Return value

An iterator to the reverse-beginning of ​​c​​ or ​​array​

Notes

In addition to being included in ​​<iterator>​​, ​​std::rbegin​​ and ​​std::crbegin​​ are guaranteed to become available if any of the following headers are included: ​ ​<array>​​, ​​<deque>​​, ​​<forward_list>​​, ​​<list>​​, ​​<map>​​, ​​<regex>​​, ​​<set>​​, ​​<string>​​, <string_view> (since C++17), ​ ​<unordered_map>​​, ​​<unordered_set>​​, and ​ ​<vector>​​.

Overloads

Custom overloads of ​​rbegin​​ may be provided for classes that do not expose a suitable ​​rbegin()​​ member function, yet can be iterated. The following overload is already provided by the standard library:



​rbegin(std::initializer_list)​


(C++14)


specializes std::rbegin 

(function)

Example




Run this code




#include <iostream>
#include <vector>
#include <iterator>


int main()

{

std::vector<int> v = { 3, 1, 4 };


auto vi = std::rbegin(v);


std::cout << *vi << '\n';


int a[] = { -5, 10, 15 };


auto ai = std::rbegin(a);


std::cout << *ai << '\n';

}


Output:




4
15


所以这篇博客就是想罗列一下C++11对vector容器的扩充。



std::vector::cbegin和std::vector::cend


这两个方法是与std::vector::begin和std::vector::end相对应的,从字面就能看出来,多了一个’c’,顾名思义就是const的意思。


所以:

std::vector::cbegin: Returns a const_iterator pointing to the first element in the container.


std::vector::cend: Returns a const_iterator pointing to the past-the-end element in the container.




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17




​<code​​​​class​​​​=​​​​"hljs cpp"​​​​>#include <iostream>​


​#include <vector>​


 


​int​​​​main ()​


​{​


​std::vector<​​​​int​​​​> myvector = {​​​​10​​​​,​​​​20​​​​,​​​​30​​​​,​​​​40​​​​,​​​​50​​​​};​


 


​std::cout << ​​​​"myvector contains:"​​​​;​


 


​for​​​​(auto it = myvector.cbegin(); it != myvector.cend(); ++it)​


​std::cout << ​​​​' '​​ ​​<< *it;​


​std::cout << ​​​​'\n'​​​​;​


 


​return​​​​0​​​​;​


​}​


​Output:​


​myvector contains: ​​​​10​​​​20​​ ​​30​​ ​​40​​ ​​50​​​​</​​​​int​​​​></vector></iostream></code>​





std::vector::crbegin和std::vector::crend


这两个方法就不解释了,与上面的相比就是多了个’r’, reverse的缩写,反转迭代器,代码就省略了。




std::vector::emplace


之前已经对emplace_back进行了讨论,其实还有一个方法叫emplace。


我想说的就是,emplace之于emplace_back就像insert之于push_back。


看英文描述就直观:




emplace:Construct and insert element


emplace_back:Construct and insert element at the end


如何使用:




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20




​<code​​​​class​​​​=​​​​"hljs cpp"​​​​>#include <iostream>​


​#include <vector>​


 


​int​​​​main ()​


​{​


​std::vector<​​​​int​​​​> myvector = {​​​​10​​​​,​​​​20​​​​,​​​​30​​​​};​


 


​auto it = myvector.emplace ( myvector.begin()+​​​​1​​​​,​​​​100​​​​);​


​myvector.emplace ( it, ​​​​200​​​​);​


​myvector.emplace ( myvector.end(), ​​​​300​​​​);​


 


​std::cout << ​​​​"myvector contains:"​​​​;​


​for​​​​(auto& x: myvector)​


​std::cout << ​​​​' '​​ ​​<< x;​


​std::cout << ​​​​'\n'​​​​;​


 


​return​​​​0​​​​;​


​}​


​Output:​


​myvector contains: ​​​​10​​​​200​​ ​​100​​ ​​20​​ ​​30​​ ​​300​​​​</​​​​int​​​​></vector></iostream></code>​





std::vector::data


Returns a direct pointer to the memory array used internally by the vector to store its owned elements.




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13




​<code​​​​class​​​​=​​​​"hljs cpp"​​​​>#include <iostream>​


​#include <vector>​


 


​int​​​​main ()​


​{​


​std::vector<​​​​int​​​​> myvector (​​​​5​​​​);​


​int​​​​* p = myvector.data();​


​*p = ​​​​10​​​​;​


​++p;​


​*p = ​​​​20​​​​;​


​p[​​​​2​​​​] = ​​​​100​​​​;​


​std::cout << ​​​​"myvector contains:"​​​​;​


​for​​​​(unsigned i=​​​​0​​​​; i<myvector.size(); ++i)=​​​​""​​​​std::cout=​​​​""​​​​<<=​​​​""​​​​'="" myvector[i];="" '​​​​\n';=​​​​""​​​​return​​​​=​​​​""​​​​0​​​​;=​​​​""​​​​}=​​​​""​​​​output:=​​​​""​​​​myvector=​​​​""​​​​contains:=​​​​""​​​​10​​​​=​​​​""​​​​20​​​​=​​​​""​​​​0​​​​=​​​​""​​​​100​​​​=​​​​""​​​​0​​​​</​​​​int​​​​></vector></iostream></code>​





std::vector::shrink_to_fit


Requests the container to reduce its capacity to fit its size.


就是减少空间




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24




​<code​​​​class​​​​=​​​​"hljs cpp"​​​​><code​​​​class​​​​=​​​​"hljs cpp"​​​​>#include <iostream>​


​#include <vector>​


​int​​​​main ()​


​{​


​std::vector<​​​​int​​​​> myvector (​​​​100​​​​);​


​std::cout << ​​​​"1. capacity of myvector: "​​ ​​<< myvector.capacity() << ​​​​'\n'​​​​;​


​std::cout << ​​​​"1. size of myvector: "​​ ​​<< myvector.size() << ​​​​'\n'​​​​;​


 


​myvector.resize(​​​​10​​​​);​


​std::cout << ​​​​"2. capacity of myvector: "​​ ​​<< myvector.capacity() << ​​​​'\n'​​​​;​


​std::cout << ​​​​"2. size of myvector: "​​ ​​<< myvector.size() << ​​​​'\n'​​​​;​


 


​myvector.shrink_to_fit();​


​std::cout << ​​​​"3. capacity of myvector: "​​ ​​<< myvector.capacity() << ​​​​'\n'​​​​;​


​std::cout << ​​​​"3. size of myvector: "​​ ​​<< myvector.size() << ​​​​'\n'​​​​;​


​return​​​​0​​​​;​


​}​


​//输出​


​1​​​​. capacity of myvector: ​​​​100​


​1​​​​. size of myvector: ​​​​100​


​2​​​​. capacity of myvector: ​​​​100​


​2​​​​. size of myvector: ​​​​10​


​3​​​​. capacity of myvector: ​​​​10​


​3​​​​. size of myvector: ​​​​10​​​​</​​​​int​​​​></vector></iostream></code></code>​



​此时,就是要明白size和capacity的区别,也就会更加理解resize和reserve的区别了!​