标准模版库概述
- 标准模版库(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 变量名;
访问:
*it;
修改:
*it = 5;
vector<int>:: iterator it;
// 访问一个迭代器指向的元素:
//* 迭代器变量名
//例如:
*it=5;
算法
- 由许多函数模版组成的集合,实现了大量通用算法,用于操控各种容器
- STL中提供的算法涉及到:比较、交换、查找、遍历、复制、修改、移除、反转、排序、合并等。大约有70种标准算法
- 算法通过迭代器来操纵容器中的元素
- 算法可以处理容器,也可以处理C语言的数组
vector
vector主要特征:
- vector 实际上就是对动态数组封装
- 可以像数组一样可以使用下标访问元素,若vector长度为n,则其下标为 0~n-1
- 根据下标访问元素效率高
- vector对象的空间随着插入删除操作自动调整
- 因为空间自动调整比较耗费时间,因此频繁插入删除的情况下,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);
vector-尾部添加元素
- 使用
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;
}
vector-任意位置插入元素
- 使用
insert()
函数向 vector 添加元素
#include<iostream>
#include<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()
获取相应位置的迭代器,begin()
是开头,end()
是尾部元素的下一个,不包含该位置
vector-用下标访问元素
使用下标[]
可以获取元素值,修改元素
#include<iostream>
#include<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); // 倒数第二位置
v1[4] = 10; // 修改index为4的元素,v1[5]=6是不可以的,超界错误
for(int i=0; i<v1.size(); i++) // v1.size()为5
{
cout << v1[i]<< " ";
}
return 0;
}
0 1 2 3 10
vector-删除尾部元素
- 使用
pop_back()
删除最后一个元素
#include<iostream>
#include<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); // 倒数第二位置
v1[4] = 10; // 修改index为4的元素,v1[5]=6是不可以的,超界错误
v1.pop_back(); // 删除10
return 0;
}
vector-删除任意元素
- 使用
erase()
删除任意位置元素 - 使用
clear()
全删除
#include<iostream>
#include<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); // 倒数第二位置
v1[4] = 10; // 修改index为4的元素,v1[5]=6是不可以的,超界错误
v1.pop_back(); // 删除10
v1.erase(v1.begin()); // 删除0
v1.erase(v1.begin(), v1.end()); // 全删
return 0;
}
也可以使用v1.clear()
全删除
vector-向量大小相关函数
v.size()
: 返回向量的大小v.max_size()
: 返回向量可容纳的最大个数v.empty()
: 返回向量是否为空v.resize(n)
: 调整向量大小,使其可以容纳n个元素,如果n<v.size(),
则删除多出来的元素; 否则,添加新元素v.resize(n, t)
: 调整向量的大小,使其可以容纳n个元素,所有新添加的元素初始化为tv.capacity()
: 获取向量的容量,再分配内存空间之前所能容纳的元素个数
vector上的迭代器
迭代器的基本操作:
向量上的迭代器定义、使用
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返回的迭代器相同
用迭代器取元素
- 将1~9加入vector,再将偶数取出
#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;
}
2 4 6 8
以迭代器为参数的插入删除函数
-
在迭代器p所指向的元素前面插入值为 t 的元素
v.insert(p, t);
-
在迭代器p所指向的元素前面插入n个值为t的新元素
v.insert(p, n, t);
-
在迭代器p所指向的元素前面插入迭代器b和e标记的范围内的元素
v.insert(p, b, e);
-
删除迭代器p指向的容器中的元素
v.erase(p);
-
删除迭代器b和e所标记范围内的元素
v.erase(b, e);
例子
vector<int> v2(3, 1); // init
vector<int> v1(4, 0);
v1.insert(v1.begin(), 5); // 在头部插入5
v1.insert(v1.end(), 7); // 在尾部插入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);
用迭代器循环删除遇到的问题
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); // 将删除的返回值赋给it
else
it++;
}
在C++11标准中,erase()
会返回一个iterator,这个iterator指向了“当前删除元素的后继元素”
在vector上应用算法
常用算法
- 排序
sort( )
- 查找
find( )
- 替换
replace( )
- 合并
merge( )
- 反序
reverse( )
- 统计
count( )
- 其他等等算法
许多算法往往以迭代器作参数。比如排序和查找都需要两个迭代器参数(表示起始位置、终止位置)
有的算法返回一个迭代器。比如find算法,在容器中查找一个元素,并返回一个指向该元素的迭代器
算法主要在头文件<algorithm>
和<numeric>
中定义
算法示例: 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; // 找到了
p = find(v.begin(), v.end(), 5); // 没有找到时,p==v.end()
if(p==v.end())
cout<<"not found\n";
return 0;
}
3
not found
算法示例: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");
sort(v.begin(), v.end()); // 排序,从小到大
vector<string>::iterator it;
for(it=v.begin(); it!=v.end(); it++)
cout<< *it <<" ";
return 0;
}
apple, candy, food
算法示例: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 << " ";
return 0;
}
5 10 10 15 20 20 25 30 40 50
其他算法示例
-
replace( first, last, old, new ) // first, last为迭代器
作用:将[ first, last )
范围内的所有值为old的替换为new -
reverse(start, end) // start, end为迭代器
作用:将序列中[start, end)
范围反转排列顺序 -
count(start, end, searchValue) // start, end为迭代器
作用:统计[start, end)
范围内等于searchValue的元素个数 -
accumulate(first, last, init) // first, last为迭代器
作用:将[ first, last )
范围内的所有值相加,再加上init后返回
STL中算法众多,算法可能丌一定适合的所有容器,使用时多查询手册