一、set和multiset基础

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

需要包含头文件:

#include <set>

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

  1. template<class _Kty,
  2. class _Pr = less<_Kty>,
  3. class _Alloc = allocator<_Kty> >
  4. class set
  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参数定义:

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

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

程序实例:

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

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

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

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

程序实例:

  1. #include <iostream>
  2. #include "print.hpp"
  3. #include <set>
  4. using namespace std;
  5. template <class T>
  6. class RuntimeCmp{
  7. public:
  8. enum cmp_mode{normal,reverse};
  9. private:
  10. cmp_mode mode;
  11. public:
  12. RuntimeCmp(cmp_mode m = normal):mode(m){}
  13. bool operator()(const T &t1,const T &t2)
  14. {
  15. return mode == normal ? t1 < t2 : t2 < t1;
  16. }
  17. bool operator==(const RuntimeCmp &rc)
  18. {
  19. return mode == rc.mode;
  20. }
  21. };
  22. typedef set<int,RuntimeCmp<int> > IntSet;
  23. void fill(IntSet& set);
  24. int main()
  25. {
  26. IntSet set1;
  27. fill(set1);
  28. PRINT_ELEMENTS(set1,"set1:");
  29. RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);
  30. IntSet set2(reverse_order);
  31. fill(set2);
  32. PRINT_ELEMENTS(set2,"set2:");
  33. set1 = set2;//assignment:OK
  34. set1.insert(3);
  35. PRINT_ELEMENTS(set1,"set1:");
  36. 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>
  37. cout << "set1 and set2 have the same sorting criterion" << endl;
  38. else
  39. cout << "set1 and set2 have the different sorting criterion" << endl;
  40. }
  41. void fill(IntSet &set)
  42. {
  43. set.insert(4);
  44. set.insert(7);
  45. set.insert(5);
  46. set.insert(1);
  47. set.insert(6);
  48. set.insert(2);
  49. set.insert(5);
  50. }

运行结果:

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

非变动性操作

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

特殊的搜寻函数

赋值

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

迭代器相关函数

元素的插入和删除

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

set提供的插入函数:

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

multiset提供的插入函数:

  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()函数:

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

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

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

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



五、set应用示例:

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

上述程序最后新产生一个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.

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:

specializes std::rbegin 

(function)

Example

Run this code
  1. #include <iostream>
  2. #include <vector>
  3. #include <iterator>
  4.  
  5. int main()
  6. {
  7. std::vector<int> v = { 3, 1, 4 };
  8. auto vi = std::rbegin(v);
  9. std::cout << *vi << '\n';
  10.  
  11.  
  12. int a[] = { -5, 10, 15 };
  13. auto ai = std::rbegin(a);
  14. std::cout << *ai << '\n';
  15. }

Output:

  1. 4
  2. 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的区别了!

【C/C++开发】容器set和multiset,C++11对vector成员函数的扩展(cbegin()、cend()、crbegin()、crend()、emplace()、data())的更多相关文章

  1. 面向UI编程:ui.js 1.0 粗糙版本发布,分布式开发+容器化+组件化+配置化框架,从无到有的艰难创造

    时隔第一次被UI思路激励,到现在1.0的粗糙版本发布,掐指一算整整半年了.半年之间,有些细节不断推翻重做,再推翻再重做.时隔今日,终于能先出来个东西了,这个版本很粗糙,主体功能大概能实现了,但是还是有 ...

  2. 【Cocos2d-x游戏开发】细数Cocos2d-x开发中那些常用的C++11知识

    自从Cocos2d-x3.0开始,Cocos2dx就正式的使用了C++11标准.C++11简洁方便的特性使程序的可拓展性和可维护性大大提高,也提高了代码的书写速度. 下面我们就来一起学习一下Cocos ...

  3. c++ stl容器set成员函数介绍及set集合插入,遍历等用法举例

    c++ stl集合set介绍 c++ stl集合(Set)是一种包含已排序对象的关联容器.set/multiset会根据待定的排序准则,自动将元素排序.两者不同在于前者不允许元素重复,而后者允许. 1 ...

  4. 容器大小的改变以及容器操作可能使迭代器失效、vector对象的容量变化

    1 改变容器的大小 我们可以使用resize来增加或缩小容器,与往常一样,array不支持resize.如果当前大小大于所要求的大小,容器后面的元素会被删除:如果当前大小小于新大小,会将新元素添加到容 ...

  5. 银弹谷零代码开发V百科|使用技巧:OMG!这些时间日期函数太好用了吧,盘它

    银弹谷零代码开发V百科|使用技巧:OMG!这些时间日期函数太好用了吧,盘它 Hello~everybody!小V又来咯!这次小V给大家带来的是零代码开发V平台常用的时间日期函数.小V知道我们平时常常会 ...

  6. Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析

    之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析,通过对LoadModel函数及 ...

  7. C++ STL学习之容器set和multiset (补充材料)

    一.set和multiset基础 set和multiset会根据特定的排序准则,自动将元素进行排序.不同的是后者允许元素重复而前者不允许. 需要包含头文件: #include <set> ...

  8. 08--STL关联容器(set/multiset)

    一:set/multiset的简介 set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列.元素插入过程是按排序规则插入,所以不能指定插入位置. set采用红黑树变体的数据结构实 ...

  9. STL标准库-容器-set与multiset

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key se ...

随机推荐

  1. WPF 精修篇 路径动画

    原文:WPF 精修篇 路径动画 路径动画 是让一个对象围绕指定Path 的运动路径 进行移动的动画 举栗子 路径动画 使用 Blend 来设置 是十分简单的 首先用工具 笔  点出一条线 新建一个圆形 ...

  2. autocomplete.js 插件的使用遇到的bug

    1. Uncaught TypeError: Cannot read property 'toLowerCase' of undefined 股票信息缺少字段(默认为三个字段,缺少P字段) 2. Ca ...

  3. linux命令之——grep详解

    grep命令用于查找文件里符合条件的字符串:也可以用于查找内容包含指定的范本样式的文件.它能使用正则表达式搜索,用于在文件中搜索指定的字符串模式,列出含有匹配模式子符串的文件名,并输出含有该字符串的文 ...

  4. 基础html页面结构

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  5. iOS开发使用Xcode的一些小技巧

    1.打开iOS项目 如果你当前目录下既有project又有workspace,你可以在终端使用命令“xed.”自动打开workspace,如果只有project,它会自动打开project. 2.清理 ...

  6. 浓缩版java8新特性

    目录 一.Lambda 1.定义/设计原因 2.结构 3.规则 4.使用 二.函数式接口 1.定义 2.设计原因 3.使用 三.方法引用 1.定义/设计原因 2.使用 四.接口的默认方法 1.定义 2 ...

  7. 配置OEL7 YUM源

    用于其他发行版如rhel.centos有时候要用到oracle linux的源来装软件比如oracle.mysql等 配置oel7源 wget http://public-yum.oracle.com ...

  8. Centos系统 tomcat 中的应用与北京时间相差8小时

    解决方法: 在 catalina.sh中的 JAVA_OPTS 加上 -Duser.timezone=GMT+08  启动时 startup.sh 会调用 catalina.sh.

  9. php-fpm编译安装脚本

      PHP是开源.轻量级.高效的开发语言,特别适合web项目开发,在中小型互联网公司中常用于开发web后端.PHP常与Nginx及MySQL数据库结合,搭建LNMP环境.以下为centos7系统下ph ...

  10. pyecharts的使用

    折线图1 import pyecharts.options as opts from pyecharts.charts import Line ​ x_data = ["Mon", ...