ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++STL标准库学习笔记(九)标准模板库STL概述

2022-01-06 15:03:24  阅读:141  来源: 互联网

标签:容器 迭代 STL 元素 back C++ 标准 算法 include


前言:

  在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来。

  在这一篇文章中,我们主要对标准模板库STL进行各方面的介绍

 

1. 泛型程序设计

C++语言的核心优势之一就是便于软件的重用

C++中有两个方面体现重用:

  1.面向对象的思想:继承与多态,标准类库

  2.泛型程序设计(generic programming)的思想:模板机制,以及标准程序库STL

  简单的说就是使用模板的程序设计法。

  将一些常用的数据结构(链表,数组,二叉树)和算法(排序,查找)写成模板,以后则不论数据结构里放的是什么对象,算法针对什么样的对象,则都不必重新实现数据结构,重新编写算法。

  标准模板库(Standard Template Library)就是一些常用数据结构和算法的模板的集合。

有了STL,不必再写大多的标准数据结构与算法,并且可以获得非常高的性能。(一般来说自己写的还没他们写的效率高)

 

2. STL中的基本的概念

  容器:可容纳各种数据类型的通用数据结构,是类模板

  迭代器:可用于依次存取容器中的元素,类似于指针

  算法:用来操作容器中的元素的函数模板

    sort()来对一个vector中的数据进行排序

    find()来搜索一个list中的对象

    算法本身与他们操作的数据的类型无关,因此他们可以从简单数组到高度复杂容器的任何数据结构上使用。

  int array[100];

  该数组就是容器,而int* 类型的指针变量就可以作为迭代器,sort算法可以作用于该容器上,对其进行排序:

  sort(array,array+70);

  这里的array就是迭代器

 

3. 容器概述

  可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模板,分为三种:

1)顺序容器

  vector(动态数组,一维数组),deque(双向队列),list(双向链表)

2)关联容器

  set,multiset,map,multimap(本质上来说都是平衡二叉树,查找效率高,自带排序)

3)容器适配器

  stack(栈),queue(队列),priority_queue(优先级队列)

  对象被插入容器中时,被插入的是对象的一个复制品。许多算法,比如排序,查找,要求对容器中的元素进行比较,有的容器本身就是排序的,所以,放入容器的对象所属的类,往往还应该重载==和<运算符。

 

4. 顺序容器简介

  容器并非排序的,元素的插入位置同元素的值无关。

  有vector,deque,list三种

  vector:

  头文件:#include<vector>

  动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)(特殊情况下是O(n)时间,在超出它预先分配的空间的时候,再增加元素就要这么多时间了,包括重新分配空间和把原来的空间的元素复制过来的时间)。在前面增删元素也是需要更多时间的,可以直接当作数组来理解。

  deque:

  头文件:#include<deque>

双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)(这玩意这样理解比较好:普通的数组是从下标0开始,但是这玩意是从n开始,所以能在n-1存元素,相应的算法相信一看到是这样存的都懂了,不过它有一点处理很有意思:在队尾存到不能再存的时候,它会把下个元素存到a[0],然后把队尾设成对应下标,相当于循环队列了)

  list:

  头文件:#include<list>

  双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。

 

5. 关联容器简介

  1、元素是排序的

  2、插入任何元素,都按相应的排序规则来确定其位置

  3、在查找时具有非常好的性能

  4、通常以平衡二叉树方式实现,插入和检索的时间都是O(log(N))

  set/multiset

  头文件:#include<set>

  set即集合。set中不允许相同元素,multiset中允许存在相同的元素。(一般来说我就是把它当集合来用,看一看哪个元素是否访问过了之类的)

  map/multimap

  头文件:#include<map>

  map与set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second,map根据first值对元素进行从小到大排序(可以自定义排序方式的),并可快速地根据first来检索元素。(有点像python里面的字典,哈哈哈,说不定是同一个东西)

  map同multimap的不同在于是否允许相同first值的元素。

 

6. 容器适配器简介

  stack

  头文件:#include<stack>

  栈。是项的有限序列,并满足序列中被删除,检索和修改的项只能是最近插入序列的项(栈顶的项)。后进先出。

  queue

  头文件:#include<queue>

  队列。插入只可以在尾部进行,删除,检索和修改只允许从头部进行。先进先出。

  priority_queue

  头文件:#include<queue>

  优先级队列。最高优先级元素总是第一个出列。

 

7. 顺序容器和关联容器都有的成员函数

  begin:返回指向容器中第一个元素的迭代器

  end:返回指向容器中最后一个元素后面位置的迭代器

  rbegin:返回容器中最后一个元素的迭代器

  rend:返回指向容器中第一个元素前面位置的迭代器(r==reverse)

  erase:从容器中删除一个或几个元素

  clear:从容器中删除所有元素

 

8. 顺序容器的常用成员函数

  front:返回容器中第一个元素的引用

  back:返回容器中最后一个元素的引用

  push_back:在容器末尾增加新元素

  pop_back:删除容器末尾的元素

  erase:删除迭代器指向的元素(可能会使得该迭代器失效),或删除一个区间,返回被删除元素后面那个元素的迭代器

 

9. 迭代器

  1、用于指向顺序容器和关联容器中的元素

  2、迭代器用法和指针类似

  3、有const和非const两种

  4、通过迭代器可以读取它指向的元素

  5、通过非const迭代器还能修改其指向的元素

 

  定义一个容器类的迭代器的方法可以是:

  容器类名::iterator 变量名;

  或:

  容器类名::const_iterator 变量名;

  访问一个迭代器指向的元素:

  *迭代器变量名

 

  迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,这时再使用它,就会出错,类似于使用NULL或未初始化的指针一样。

  样例:

 1 #include<vector>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 int main(int argc, char const *argv[])
 6 {
 7     vector<int> v;//一个存放int元素的数组,一开始里面没有元素
 8     v.push_back(1);
 9     v.push_back(2);
10     v.push_back(3);
11     v.push_back(4);
12     vector<int>::const_iterator i;//常量迭代器
13     for ( i = v.begin(); i != v.end(); i++)
14     {
15         cout<<*i<<",";
16     }
17     cout<<endl;//输出:1,2,3,4,
18     vector<int>::reverse_iterator r;//反向迭代器
19     for (r = v.rbegin(); r != v.rend(); r++)
20     {
21         cout<<*r<<",";
22     }
23     cout<<endl;//输出:4,3,2,1,
24     vector<int>::iterator j;//非常量迭代器
25     for ( j = v.begin(); j != v.end(); j++)
26     {
27         *j = 100;
28     }
29     for ( i = v.begin(); i != v.end(); i++)
30     {
31         cout<<*i<<",";
32     }
33     cout<<endl;//输出:100,100,100,100,
34     return 0;
35 }
样例

 

10. 双向迭代器

  若p和p1都是双向迭代器,则可对p,p1进行以下操作:

  ++p,p++:使p指向容器中下一个元素

  --p,p--:使p指向容器中上一个元素

  *p:取p指向的元素(可以用来修改元素的值)

  p = p1:赋值

  p == p1,p!=p1:判断是否相等、不等

 

正确的遍历list的方法:

 

  list<int> v;

 

  list<int>::const_iterator ii;

 

  for( ii = v.begin(); ii != v.end(); ++ii)

 

    cout<<*ii

错误的做法:

  for( ii = v.begin(); ii < v.end(); ++ii)

    cout<<*ii

  for( int i = 0; i < v.size(); ++i)

    cout<<v[i]

  (双向迭代器不支持<,list没有[]成员函数)

 

11. 随机访问迭代器

  若p和p1都是随机访问迭代器,则可对p,p1进行以下操作:

  双向迭代器的所有操作

  p += i:将p向后移动i个元素

  p -= i:将p向前移动i个元素

  p + i:值为:指向p后面的第i个元素的迭代器

  p - i:值为:指向p前面的第i个元素的迭代器

  p[i]:值为:p后面的第i个元素的引用

  p < p1,p <= p1,p > p1,p >= p1

 

  遍历vector/deque有以下几种做法:

 

 1 #include<vector>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 int main(int argc, char const *argv[])
 6 {
 7     vector<int> v;
 8     v.push_back(1);
 9     v.push_back(2);
10     v.push_back(3);
11     v.push_back(4);
12     
13     for (int ii = 0; ii < v.size(); ii++)
14     {
15         cout<<v[ii];//根据下标随机访问
16     }
17     cout<<endl;
18     vector<int>::const_iterator i;
19     for ( i = v.begin(); i != v.end(); i++)
20     {
21         cout<<*i;//i != v.end;
22     }
23     cout<<endl;
24     for ( i = v.begin(); i < v.end(); i++)
25     {
26         cout<<*i;//i < v.end;
27     }
28     cout<<endl;
29     i = v.begin();
30     while (i < v.end())
31     {
32         cout<<*i;
33         i = i+2;//间隔一个输出
34     }
35     
36     return 0;
37 }
样例

 

12. 容器对应的迭代器

  vector:随机访问迭代器

  deque:随机访问迭代器

  list:双向迭代器

  set/multiset:双向迭代器

  map/multimap:双向迭代器

  stack:不支持迭代器

  queue:不支持迭代器

  priority_queue:不支持迭代器

  (这样看来其实底层是数组实现的就是随机访问迭代器,底层不是数组的是双向迭代器,最后队列和栈因为不提供逐个访问的功能就不能用迭代器了)

  注:有的算法,例如sort,binary_search需要通过随机访问迭代器来访问容器中的元素,那么list以及关联容器就不支持该算法。

 

13. 算法简介

  1、算法就是一个个函数模板,大多数在<algorithm>中定义

  2、STL中提供能在各种容器中通用的算法,比如查找,排序等

  3、算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器。比如,排序和查找

  4、有的算法返回一个迭代器。比如find()算法,在容器中查找一个元素,并返回一个指向该元素的迭代器

  5、算法可以处理容器,也可以处理普通数组

 

14. 算法示例:find()

  template<class init, class T>

  init find(init first, init last, const T& val)

  1、first和last这两个参数都是容器的迭代器,它们给出了容器中的查找区间起点和终点[first,last)。区间的起点是位于查找范围之中的,而终点不是。find在[first,last)查找等于val的元素

  2、用 == 运算符判断相等

  3、函数返回值是一个迭代器。如果找到,则返回该迭代器指向被找到的元素。如果找不到,则该迭代器等于last

样例:

 

 1 #include<vector>
 2 #include<algorithm>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 int main(int argc, char const *argv[])
 7 {
 8     int array[10] = {10,20,30,40};
 9     vector<int> v;
10     v.push_back(1);
11     v.push_back(2);
12     v.push_back(3);
13     v.push_back(4);
14     vector<int>::iterator p;
15     p = find(v.begin(),v.end(),3);
16     if (p != v.end())
17     {
18         cout<<*p<<endl;//输出:3
19     }
20     p = find(v.begin(),v.end(),9);
21     if (p == v.end())
22     {
23         cout<<"not found"<<endl;//输出:not found
24     }
25     p = find(v.begin()+1,v.end()-2,1);//整个容器:[1,2,3,4],查找区间:[2,3)
26     if (p != v.end())
27     {
28         cout<<*p<<endl;//输出:3//v.end()指向的是3
29     }
30     int *pp = find(array,array+4,20);//数组名是迭代器
31     cout<<*pp<<endl;//输出:20
32     return 0;
33 }
样例

(注意:STL中只要提到区间,那么它们都是左闭右开的)

 

15. STL中“大”,“小”的概念

  1、关联容器内部的元素是从小到大排序的

  2、有些算法要求其操作的区间是从小到大排序的,称为“有序区间算法”,如:binary_search

  3、有些算法会对区间进行从小到大排序,称为“排序算法”,如:sort(这里的大和小的定义可以自己设定,可以看我以前的笔记)

  4、还有一些其他算法会用到“大”,“小”的概念

  在使用STL时,在缺省的情况下,以下三个说法等价:(这里的缺省指没有自己定义的情况下)

    1)x比y小

    2)表达式“x<y”为真

    3)y比x大

 

16. STL中相等的概念

  有时,“x和y相等”等价于“x==y为真”

  例:

  在未排序的区间上进行的算法,如排序查找find

  有时“x和y相等”等价于“x小于y和y小于x同时为假”(这里的小于也是可以自定义的,不是严格意义上的小于,比如我设定大于为“x个位数大于y则x>y”,那么在这种情况下11等于1)

  例:

  有序区间算法,如binary_search

  关联容器自身的函数成员find

 

17. STL中“相等”的概念的演示

  样例:

 

 1 #include<algorithm>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class A
 6 {
 7     int v;
 8     public:
 9         A(int n):v(n){}
10         bool operator <(const A & a2)const
11         {
12             cout<<v<<"<"<<a2.v<<"?"<<endl;
13             return false;//总是不成立
14         }
15         bool operator ==(const A & a2)const
16         {
17             cout<<v<<"=="<<a2.v<<"?"<<endl;
18             return v == a2.v;
19         }
20 };
21 int main(int argc, char const *argv[])
22 {
23     A a[] = {A(1),A(2),A(3),A(4),A(5)};
24     cout<<binary_search(a,a+4,A(9));//折半查找
25     /*
26     输出:
27     3<9?
28     2<9?
29     1<9?
30     9<1?
31     1
32     */
33     return 0;
34 }

  可见binary_search是用的大小判断来找的,所以这里找到了

 

后记:

  这一篇好长好长啊,估计不会有人看完吧,其实这是考试周,那我现在在干嘛,考试周不好好复习在这捣鼓这些东西,还捣鼓了两个下午(*愤怒*)!明白了,这就去复习,再学这些东西我考试就要挂科了,大家千万不要模仿,感谢大家读到这里,下次再见(下次估计就是考试结束之后了)

标签:容器,迭代,STL,元素,back,C++,标准,算法,include
来源: https://www.cnblogs.com/AwakeFantasy/p/15771135.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有