以下为个人学习笔记和习题整理
课程:计算机程序设计(C++)- 西安交通大学 @ 中国大学 MOOC
https://www.icourse163.org/course/XJTU-46006
# 课堂笔记
# 函数模板
函数重载是最佳方案吗?
假如设计一个求两参数最大值的函数,在实践中可能需要定义四个函数:
int max ( int a, int b ) { return ( a > b ) ? a , b; } | |
long max ( long a, long b ) { return ( a > b ) ? a , b;} | |
double max ( double a, double b ) { return ( a >b)? a , b; } | |
char max ( char a, char b ) { return ( a > b ) ? a , b; } |
这些函数几乎相同,唯一的区别就是形参类型不同
需要事先知道有哪些类型会使用这些函数,对于未知类型,比如 string 这些函数不起作用。
专家的解决方案
template < class T > //class 某种类型 | |
T max(T a , T b) | |
{ | |
return ( a > b ) ? a , b; | |
} |
T 是什么?
答:是可以比较大小的某种类型。这种抽象的东西是什么?
答:就像一个模板,所以叫函数模板
函数模板是用类型做参数,设计出的通用的函数。
template <class T1,class T2,,,> | |
函数返回类型 函数名(函数参数表) | |
{ | |
// 函数模板定义 | |
//T1,T2 等等,用在其中 | |
} |
其中 template 表示定义的是模板, < > 里是模板的类型参数,可以是一个或多个。
# 模板工作方式
- 函数模板只是一个抽象说明,不能直接执行,需要实例化为模板函数后才能执行。
- 在说明了一个函数模板后,当编译系统发现有一个对应的函数调用时,将根据实参中的类型来确认是否匹配函数模板中对应的形参,然后生成一个重载函数。
- 函数模板如下:
template <class T> | |
T Max(T a,T b) | |
{ | |
return a>b? a:b; | |
} |
- 编译时发现:
Max(6 , 7); |
- 编译系统生成:
int Max(int a, int b) | |
{ | |
return a>b? a:b; | |
} |
# 例子:求数组中的最小值
#include <iostream> | |
template <class T> | |
T min(T a[],int n) | |
{ | |
int i; | |
T minv=a[0]; | |
for( i=1; i<n; i++) | |
{ | |
if(minv>a[i]) | |
{ | |
minv=a[i]; | |
} | |
} | |
return minv; | |
} | |
void main() { | |
int a[]={1,3,0,2,7,6,4,5,2}; | |
double b[]={1.2,-3.4,6.8,9,8}; | |
cout<<"a数组的最小值为: "<<min(a,9)<< endl; | |
cout<<"b数组的最小值为: "<<min(b,4)<<endl; | |
} | |
//a 数组的最小值为:0 | |
//b 数组的最小值为:-3.4 |
# 类模板
从栈说起
栈是一种线性结构
栈限制在结构的一端进行插入和删除操作
允许插入和删除操作的一端称为栈顶,另一端称为栈底
| ⤵️进栈 出栈⤴️ | |
|---|---|
| 栈顶 top ➡️ | a3 |
| a2 | |
| 栈底 | a1 |
- 建立
int型的栈类属性(数据成员):
int型数组(存储)top(栈顶位置)操作(成员函数):
进栈(插入)
出栈(删除)
class stack | |
{ | |
int dat[100]; // 设元素数 & lt;=100 | |
int top; | |
public: | |
void push(int e) { // 进栈 | |
将数据放入dat; | |
改top; // 向上移 | |
} | |
int pop() { // 出栈 | |
改top; // 向下移 | |
返回删除的值; | |
} | |
} |
- 建立抽象的栈类模板
template <class T> // 此处 class 指某种类型,不是 C++ 中的类 | |
class stack | |
{ | |
T dat[100]; | |
int top; | |
public: | |
// 进栈 | |
void push(T e) | |
{ …… } | |
// 出栈 | |
T pop( ) | |
{ …… } | |
} |
类是对问题空间的抽象,而类模板则是对类的抽象,是对一批仅仅成员数据类型不同的类的抽象。
程序中可以首先定义一个类模板,然后通过使用不同的实参生成不同的类。
template <class <类型参数>> | |
class <类名> | |
{ | |
…… | |
}; |
类模板名 <数据类型> 对象名; | |
// 例如 | |
stack <int> s1; // 定义一个整数类型栈 | |
stack <char> s2; // 定义一个字符类型栈 |
在 C++ 的标准模板库 (STL) 中,定义了大量类模板(其中也包括栈),使用这些优秀的类模板可以我们提高编程效率,提高程序可靠性。
# 标准模板库 STL
Standard Template Library,STL 是一个具有工业强度的,高效的 C++ 程序库。
它实现了诸多在计算机科学领域里常用的基本数据结构和基本算法。
STL 主要包含了容器、算法、迭代器。
STL 系由 Alexander Stepanov 和 Meng Lee 等人创造于惠普实验室。
STL 于 1994 年 2 月年正式成为 ANSI/ISO C++ 的一部份。
# 容器
是容纳、包含相同类型元素的对象,主要用类模板实现。
序列型容器:
容器中的元素按线性结构组织起来,可以逐个读写元素。
主要代表有vector向量、deque双端队列 、list双向链表。关联型容器:
关联容器通过键key存储和读取元素。
主要有map映射、set集合等。容器适配器:
是对前面提到的某些容器(如vector)进行再包装,使其变为另一种容器。
典型的有栈stack、队列queue等。
# 迭代器
是用于确定元素位置的数据类型,可用来遍历容器中的元素。
通过迭代器可以读取、修改它指向的元素,它的用法和指针类似。
每一种容器都定义了一种迭代器。
定义一个容器类的迭代器的方法可以是:
容器类名<元素类型>::iterator 变量名; | |
vector<int>::iterator it; |
- 访问一个迭代器指向的元素:
*迭代器变量名 | |
*it=5; |
# 算法
由许多函数模版组成的集合,实现了大量通用算法,用于操控各种容器。
STL 中提供的算法涉及到:比较、交换、查找、遍历、复制、修改、移除、反转、排序、合并等。大约有 70 种标准算法。
算法通过迭代器来操纵容器中的元素。
算法可以处理容器,也可以处理 C 语言的数组。
# vector 容器
# 主要特征
vector实际上就是对动态数组封装可以像数组一样可以使用下标访问元素
若vector长度为n,则其下标为0~n-1,根据下标访问元素效率高vector对象的空间随着插入删除操作自动调整
因为空间自动调整比较耗费时间,因此频繁插入删除的情况下,vector效率稍差
# 对象创建
// 创建一个空向量 | |
vector<int> v1; //int 类型向量 | |
vector<string> s1; //string 类型向量 | |
// 从已有向量复制创建向量 | |
vector<int> v2(v1); // 拷贝 v1 内容到 v2(拷贝构造函数) | |
// 创建 10 个元素的向量 | |
vector<string> s2(10); | |
// 创建 10 个元素的向量,所有元素都是 1.5 | |
vector<double> v3(10, 1.5); | |
// 创建向量指针 | |
vector<int> *pvec = new vector<int>(10, -5); |
# 添加元素
# push_back() 尾部添加元素
使用 push_back() 函数向 vector 尾部添加元素
#include<iostream> | |
#include<vector> | |
using namespace std; | |
int main() | |
{ | |
vector<int> v1; | |
v1.push_back(1); | |
v1.push_back(2); | |
return 0; | |
} |
| 1 | |||
| 1 | 2 |
# insert() 任意位置插入元素
使用 insert() 函数向 vector 添加元素
#include<iostream> | |
#include<vector> // 使用 vector 必备 | |
using namespace std; | |
int main() | |
{ | |
vector<int> v1; | |
v1.push_back(1); | |
v1.push_back(2); | |
v1.insert(v1.begin(),0); // 头部插入 | |
v1.insert(v1.end(), 4); // 尾部插入 | |
v1.insert(v1.end()-1,3); // 倒数第二位置 | |
return 0; | |
} |
v1.begin(),v1.end()获取相应位置的迭代器
| 1 | |||||
| 1 | 2 | ||||
| 0 | 1 | 2 | |||
| 0 | 1 | 2 | 4 | ||
| 0 | 1 | 2 | 3 | 4 |
# 访问元素
使用 [下标] 可以获取元素值,修改元素
#include<iostream> | |
#include<vector> | |
using namespace std; | |
int main() | |
{ | |
vector<int> v1; | |
v1.insert(v1.end()-1,3); // 倒数第二位置 | |
v1[4]=10; //v1 [5]=6; 超界错误 | |
for(int i=0; i<v1.size(); i++) { | |
//v1.size () 为 5 | |
cout<<v1[i]<<" "; | |
} | |
return 0; | |
} |
| 1 | |||||
| 1 | 2 | ||||
| 0 | 1 | 2 | |||
| 0 | 1 | 2 | 4 | ||
| 0 | 1 | 2 | 3 | 10 |
# 删除元素
# pop_back() 删除最后一个元素
#include<iostream> | |
#include<vector> | |
using namespace std; | |
int main() { | |
vector<int> v1; | |
…… …… | |
v1[4]=10; | |
v1.pop_back(); // 删除 10 | |
return 0; | |
} |
| 1 | |||||
| 1 | 2 | ||||
| 0 | 1 | 2 | |||
| 0 | 1 | 2 | 4 | ||
| 0 | 1 | 2 | 3 | 10 | |
| 0 | 1 | 2 | 3 |
# erase() 删除任意位置元素
#include<iostream> | |
#include<vector> | |
using namespace std; | |
int main() { | |
vector<int> v1; | |
…… …… | |
v1.pop_back(); // 删除 10 | |
v1.erase(v1.begin()); // 删除 0 | |
v1.erase(v1.begin(), v1.end()); // 全删 | |
return 0; | |
} |
# clear() 删除全部元素
v1.clear()
| 1 | |||||
| 1 | 2 | ||||
| 0 | 1 | 2 | |||
| 0 | 1 | 2 | 4 | ||
| 0 | 1 | 2 | 3 | 10 | |
| 1 | 2 | 3 | |||
# 向量大小相关函数
v.size() | 返回向量的大小 |
v.max_size() | 返回向量可容纳的最大个数 |
v.empty() | 返回向量是否为空 |
v.resize(n) | 调整向量大小,使其可以容纳 n 个元素,如果 n<v.size() , 则删除多出来的元素;否则,添加新元素 |
v.resize(n,t) | 调整向量的大小,使其可以容纳 n 个元素,所有新添加的元素初始化为 t |
v.capacity() | 获取向量的容量,再分配内存空间之前所能容纳的元素个数 |
| 0 | 1 | 2 | ... | 23 | ... | ... | |||
⬆️ | ⬆️ | ||||||||
# 迭代器
# 基本操作
vector<int>::iterator it; | |
*it = 5; |
vector 上迭代器支持随机访问:
- 提供读写操作
- 并能在数据中随机移动(前后,跳跃式)
用加、减操作移动迭代器:
it++; ++it; // 指向下一元素 | |
it--; --it; // 指向前一元素 | |
it + i // 返回指向 it 后面的第 i 个元素的迭代器 | |
it - i // 返回指向 it 前面的第 i 个元素的迭代器 |
用
<,<=,>,>=,==,!=判断迭代器前后、相等关系:
it1 < it2 // 表示 it1 在 it2 之前 |
# begin() 和 end() 函数
每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。
如果容器中有元素,由 begin 返回的迭代器指向第一个元素:
it = v1.begin(); // 指向 v1 [0] |
由 end 返回的迭代器指向 vector 的末端元素的下一个。
通常称为超出末端迭代器,表明它指向了一个不存在的元素。
it = v1.end(); // 指向末端元素的下一个 |
如果 vector 为空, begin 返回的迭代器与 end 返回的迭代器相同
# 用迭代器读取元素
#include<iostream> | |
#include<vector> | |
using namespace std; | |
int main() { | |
vector<int> v1; | |
for(int i=1; i<10; i++) | |
{ | |
v1.push_back(i); // 添加 1~9 | |
} | |
vector<int>::iterator it; | |
for(it=v1.begin(); it<v1.end(); it++) { | |
if(*it%2==0) { | |
cout<<*it<<" "; | |
} | |
} | |
return 0; | |
} |
# 以迭代器为参数的插入删除函数
v.insert(p,t) | 在迭代器 p 所指向的元素前面插入值为 t 的元素 |
v.insert(p,n,t) | 在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素 |
v.insert(p,b,e) | 在迭代器 p 所指向的元素前面插入迭代器 b 和 e 标记的范围内的元素 |
v.erase(p) | 删除迭代器 p 指向的容器中的元素 |
v.erase(b,e) | 删除迭代器 b 和 e 所标记范围内的元素 |
vector<int> v2(3, 1); | |
vector<int> v1(4, 0); | |
// 在头部插入 5 | |
v1.insert(v1.begin(), 5); | |
// 在尾部插入 7 | |
v1.insert(v1.end(), 7 ); | |
// 在下标为 4 处插入 9 | |
vector<int>::iterator it = v1.begin() + 4; | |
v1.insert(it, 9 ); | |
// 删除偶数元素 | |
for(it=v1.begin(); it<v1.end(); ) { | |
if(*it%2==0) | |
{ | |
it=v1.erase(it); | |
} | |
else | |
{ | |
it++; | |
} | |
} | |
// 将 v1 的一部分拷贝到 v2 | |
v2.insert(v2.begin(),v1.begin(),v1.begin()+2); |
| v2 | 1 | 1 | 1 | |||||
| v1 | 0 | 0 | 0 | 0 | ||||
| v1 | 5 | 0 | 0 | 0 | 0 | |||
| v1 | 5 | 0 | 0 | 0 | 0 | 7 | ||
| v1 | 5 | 0 | 0 | 0 | 9 | 0 | 7 | |
| v1 | 5 | 9 | 7 | |||||
| v2 | 5 | 9 | 1 | 1 | 1 |
# 用迭代器循环删除的一个问题
vecotr<int>::iterator it = vc.begin(); | |
for( ; it != vc.end(); it++ ) | |
{ | |
if( ***** ) { | |
vc.erase(it); | |
} | |
} |
原因:
erase()删除元素后,it失效,并不是指向下一个元素
for(it=v1.begin(); it<v1.end(); ) { | |
if(*******) | |
{ | |
it=v1.erase(it); | |
} | |
else | |
{ | |
it++; | |
} | |
} |
在
C++ 11标准中,erase()会返回一个iterator,这个iterator指向了 “当前删除元素的后继元素”。
# 算法应用
算法主要在头文件 <algorithm> 和 <numeric> 中定义
| 功能 | 常用算法 |
|---|---|
| 排序 | sort() |
| 查找 | find() |
| 替换 | replace() |
| 合并 | merge() |
| 反序 | reverse() |
| 统计 | count() |
| 其他等等算法 |
许多算法往往以迭代器作参数。
比如排序和查找都需要两个迭代器参数(表示起始位置、终止位置)。有的算法返回一个迭代器。
比如find算法,在容器中查找一个元素,并返回一个指向该元素的迭代器。
# 查找 find()
简化形式: find(first, last, val)
first和last这两个参数都是容器的迭代器,它们给出了容器中的查找区间起点和终点。
这个区间是个左闭右开的区间
[first,last),即区间的起点是位于查找范围之中的,而终点不是
val参数是要查找的元素的值。函数返回值是一个迭代器。
如果找到,则该迭代器指向被找到的元素。
如果找不到,则该迭代器指向查找区间终点。
#include <vector> | |
#include <algorithm> // 算法头文件 | |
#include <iostream> | |
using namespace std; | |
int main() { | |
vector <int> v(5,3);// 一个容器,5 个 3 | |
vector <int>::iterator p; // 迭代器 | |
p = find(v.begin(),v.end(),3); | |
if( p!=v.end() ) | |
{ | |
cout<<*p<< endl; | |
} | |
// 3 | |
p = find(v.begin(),v.end(),5); | |
if( p==v.end() ) | |
{ | |
cout<<"not found\n"; | |
} | |
// not found | |
return 0; | |
} |
# 排序 sort()
简化形式: void sort( first, last )
first和last这两个参数都是容器的迭代器,它们给出了容器中的查找区间起点和终点。
#include <vector> | |
#include <algorithm> | |
#include <iostream> | |
#include <string> | |
using namespace std; | |
int main() { | |
vector <string> v; | |
v.push_back("food"); | |
v.push_back("candy"); | |
v.push_back("apple"); | |
// food candy apple | |
sort(v.begin(), v.end()); | |
vector <string>::iterator it; | |
for(it=v.begin(); it!=v.end();it++) { | |
cout<< *it <<" "; | |
} | |
// apple candy food | |
return 0; | |
} |
# 合并 merge()
形式: merge(f1, e1, f2, e2, p)
f1、e1、f2、e2、p都是迭代器- 将有序序列
v1中[f1, e1)和有序序列v2中[f2, e2)合并成有序序列,存入p的前面
#include <iostream> | |
#include <algorithm> | |
#include <vector> | |
using namespace std; | |
int main() { | |
int A[] = {5,10,15,20,25}; | |
int B[] = {50,40,30,20,10}; | |
vector<int> v(10); | |
vector<int>::iterator it; | |
sort(A, A+5); | |
sort(B, B+5); | |
merge(A, A+5, B, B+5, v.begin()); | |
for(it=v.begin(); it!=v.end(); ++it) | |
{ | |
cout<<*it<<" "; | |
} | |
// 5 10 10 15 20 20 25 30 40 50 | |
return 0; | |
} |
# 其他算法
replace(first, last, old, new)first,last为迭代器
作用:将[first,last)范围内的所有值为old的替换为newreverse(start, end)start,end为迭代器
作用:将序列中[start, end)范围反转排列顺序count(start, end, searchValue)start,end为迭代器
作用:统计[start, end)范围内等于searchValue的元素个数accumulate(first, last, init)first,last为迭代器
作用:将[first,last)范围内的所有值相加,再加上init后返回
# 序列型容器
# 类型
# vector 向量
定义在头文件 <vector>
实际上就是个动态数组。
随机存取任何元素都能在常数时间完成。
在尾端增删元素具有较佳的性能。
向量 vector
# deque 双端队列
定义于头文件 <deque>
也是个动态数组。
随机存取任何元素都能在常数时间完成,但性能次于 vector。
在两端增删元素具有较佳的性能。
双端队列 deque
# list 双向链表
定义于头文件 <list>
任意位置插入和删除元素的效率都很高
不支持随机存取
每个元素还有指针占用额外空间
双向链表 list
在序列容器中,元素的插入位置同元素的值无关
# 选用
如果程序要求随机访问元素,则应用
vector或者deque容器如果程序必须在容器中间位置插入或删除元素,则应采用
list容器如果程序不是在容器的中间位置,而是在容器的首部或尾部插入或删除元素,则应采用
deque容器
# 初始化
vector<int> vec; | |
list<string> list1; | |
deque<float> deq; |
vector<int> vec1; | |
vector<int> vec2(vec1); | |
list<string> list1; | |
list<string> list2(list1); | |
deque<float> deq1; | |
deque<float> deq2(deq1); |
vector<string> vec(10); | |
list<int> list1(10); | |
deque<string> deq(10); |
vector<string> vec(10,"hi"); | |
list<int> list1(10, 1); | |
deque<string> deq(10, "hi"); |
# 添加元素
c.push_back(t)
在容器c的尾部添加值为t的元素
返回void类型c.push_front(t)
在容器c的前端添加值为t的元素
返回void类型
只适用于list和dequec.insert(p,t)
在迭代器p所指向的元素前面元素t
返回指向新添加元素的迭代器c.insert(p,n,t)
在迭代器p所指向的元素前面插入n个值为t的新元素
返回void类型c.insert(p,b,e)
在迭代器p所指向的元素前面插入迭代器b和e标记的范围内的元素
返回void类型
# 访问元素
c.back()
返回容器c的最后一个元素的引用c.front()
返回容器 c 的第一个元素的引用c[n]
返回下标为n的元素的引用(0 <= n < c.size())
只适用于vector和deque容器c.at[n]
返回下标为n的元素的引用(0 <= n < c.size())
只适用于vector和deque容器
# 删除元素
c.pop_back()
删除容器c的最后一个元素c.pop_front()
删除容器c的第一个元素
只适用于deque和list容器c.erase(p)
删除迭代器p指向的容器中的元素c.erase(b,e)
删除迭代器b和e所标记范围内的元素c.clear()
删除容器中所有的元素
# 关联型容器
# 特征
STL 提供了 4 个关联容器,包括: map 映射、 multimap 多重映射、 set 集合、 multiset 多重集合。
map 、 multimap 的元素由 key , value 二元组构成,其中键必须是唯一的。

map 映射 & multimap 多重映射
set 、 multiset 相当于只有键 key 、没有对应值 value 的 map 和 mulitimap 。
set 支持通过键实现的快速读取,元素唯一。
A B C D E F
multiset 支持同一个键多次出现的 set 类型。
A A C C E E
# 与序列容器的差别
关联容器是通过键 key 存储和读取元素。
顺序容器则通过元素在容器中的位置顺序存储和访问元素。
map 和 set 的底层机制都是通过一种称为 “红黑树” 的数据结构存取数据,这使得它们的数据存取效率相当高。
“红黑树” 是一种常见的数据结构。
# map 容器
# pair 类型
pair 类定义在 <utility> 头文件中。pair 是一个类模板,它将两个值组织在一起,这两个值的类型可不同。
可以通过 first 和 second 公共数据成员来访问这两个值。pair 对象常常作为元素,被添加到 map 中。
pair<int, string> mypair(5 , "Jack"); // 调用构造函数 | |
pair<int, string> otherPair; // 直接赋值 | |
otherPair.first = 6; | |
otherPair.second = "Mike"; |
函数模板 make_pair() 能从两个变量构造一个 pair
pair<int, int > aPair = make_pair( 5, 10 ); |
# 创建及添加元素
map 类定义在 <map> 头文件中。
map<int, string> StuInfo; |
定义了一个用
int作为键、相关联string为值的map
pair<int, string> mypair(1, "Tom"); | |
StuInfo.insert(mypair); | |
StuInfo.insert(pair<int, string>(5, "Jack")); |
# 使用运算符 []
修改元素的值
键不可修改StuInfo[1] = "Jim";
因为键为
1的元素存在,因此修改元素。添加元素
StuInfo[2] = "Lily";
先查找主键为
2的项,没找到,因此添加这个键为2的项。取得元素的值
cout<<StuInfo[5] ; // 输出键 5 对应的值
# find() 查找元素
用 find() 查找 map 中是否包含某个关键字
若查找成功则返回目标项的迭代器,否则返回 StuInfo.end() 迭代器。
int target = 3; | |
map<int,string>::iterator it; | |
it = StuInfo.find(target); // 查找关键字 target | |
if( it == StuInfo.end() ){ | |
cout<<"not existed!"; | |
}else{ | |
cout<<"find it!"<<endl; | |
} |
# 删除元素
- 通过
erase()函数按照关键字删除
若删除成功,返回1,否则返回0// 删掉关键字 "1" 对应的条目int r = StuInfo.erase(1);
- 用
clear()清空mapStuInfo.clear();
# 迭代器
map<int,string> m; | |
map<int,string>::iterator it; | |
for(it = m.begin();it < m.end(); it++) | |
{ ***** } |
map<int,string> m; | |
map<int,string>::iterator it; | |
for(it = m.begin();it != m.end(); it++) | |
{ ***** } |
#include <iostream> | |
#include <string> | |
#include <utility> | |
#include <map> | |
using namespace std; | |
int main () { | |
map<int,string> StuInfo; | |
StuInfo.insert(pair<int, string>(1, "Tom")); | |
StuInfo.insert(pair<int, string>(5, "Jack")); | |
StuInfo[2]="Lily"; | |
map<int,string>::iterator it; | |
for(it = StuInfo.begin();it != StuInfo.end(); it++) | |
{ | |
cout<<(*it).first<<" "<<(*it).second<<endl; | |
} | |
return 0; | |
} | |
// 1 Tom | |
// 2 Lily | |
// 5 Jack |
# 迭代器
STL 中的迭代器按功能由弱到强分为 5 种:
- 输入:
Input iterators提供对数据的只读访问。 - 输出:
Output iterators提供对数据的只写访问。 - 正向:
Forward iterators提供读写操作,并能一次一个地向前推进迭代器。 - 双向:
Bidirectional iterators提供读写操作,并能一次一个地向前和向后移动。 - 随机访问:
Random access iterators提供读写操作,并能在数据中随机移动。
- 输入:
编号大的迭代器拥有编号小的迭代器的所有功能,能当作编号小的迭代器使用。
# 不同迭代器能进行的操作
- 所有迭代器:
++p,p++ - 输入迭代器:
*p,p = p1,p==p1,p!=p1 - 输出迭代器:
*p,p = p1 - 正向迭代器: 上面全部
- 双向迭代器: 上面全部,
--p,p--, - 随机访问迭代器: 上面全部,以及:
p += i,p -= i,p + i返回指向p后面的第i个元素的迭代器p – i返回指向p前面的第i个元素的迭代器p < p1,p <= p1,p > p1,p >= p1
# 容器所支持的迭代器类别
| 容器 | 迭代器类别 |
|---|---|
vector | 随机 |
deque | 随机 |
list | 双向 |
set/multiset | 双向 |
map/multimap | 双向 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
关联容器支持双向迭代器,它支持:
*、++、--、=、==、!=。
不支持:<、<=、>=、>
# 容器适配器
容器适配器将其他容器加以包装、改造,变成新的容器。
实质上是一种受限容器。
典型容器适配器: stack 栈、 queue 队列
# stack 堆栈
栈是限制在结构的一端进行插入和删除操作。
允许进行插入和删除操作的一端称为栈顶,另一端称为栈底。
编程时加入下列语句:
#include<stack> |
栈的常用函数有:
push(elem) | 将元素 elem 入栈 |
pop() | 栈顶元素出栈 |
top() | 求栈顶元素 |
empty() | 判断栈是否空 |
size() | 求栈内元素个数 |
#include<iostream> | |
#include<stack> | |
using namespace std; | |
int main() | |
{ | |
// 定义栈 s | |
stack<int> s; | |
// 入栈 | |
s.push(1); | |
s.push(2); | |
s.push(3); | |
s.push(9); | |
// 读栈顶元素 | |
cout<<"栈顶元素:"<<s.top()<<endl; | |
// 返回元素个数 | |
cout<<"元素数量:"<<s.size()<<endl; | |
cout<<"出栈过程:"; | |
while(s.empty()!=true) // 栈非空 | |
{ | |
cout<<s.top()<<" "; // 读栈顶元素 | |
s.pop(); // 出栈,删除栈顶元素 | |
} | |
return 0; | |
} | |
// 栈顶元素:9 | |
// 元素数量:4 | |
// 出栈过程:9 3 2 1 |
# queue 队列
只能在一端进行插入、在另一端迚行删除操作的线性结构。
加入下列语句:
#include<queue> |
队列的常用函数有:
push() | 入队 |
pop() | 出队 |
front() | 读取队首元素 |
back() | 读取队尾元素 |
empty() | 判断队列是否空 |
size() | 求队列长度 |