- STL(Standard Template Library,标准模板库)是 C++ 模板类集合,提供了统一的编程书籍结构和函数。
- STL 是容器类、算法和迭代器的库,是一个通用的库,组件都是参数化的。
- STL 有 4 个组件:算法、容器、函数和迭代器。
- 定义了 STL 的基础性的算法(均为函数模板),用于给定范围的元素。 C++98 中有 70 个算法模板函数,C++11 增加了 20 个算法模板函数,其中有 5 个定义在
numeric
头文件,其他定义在 algorithm
中
numeric
头文件包含的算法模板函数
- accumulate:累加序列值
- adjacent_difference:计算相邻两项的差值
- inner_product:计算输入序列的内积
- partial_sum:计算序列的部分累加值
- iota:保存增加的连续值序列
- 函数原型:
template <class RandomAccessIterator> void sort (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare> void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
- 底层使用快排实现。
- 算法复杂度: O(N*lgN)。
#include <iostream>
#include <algorithm>
using namespace std;
void show(int a[])
{
for(int i=0; i<10; ++i)
cout << a[i] << " ";
cout << endl;
}
int main()
{
int a[10]={1, 5, 8, 9, 6, 7, 3, 4, 2, 0};
cout << "\n The array before sorting is : ";
show(a);
sort(a,a+10);
cout << "\n The array after sorting is : ";
show(a);
return 0;
}
- 广泛使用的搜索算法是二分搜索,前提是数组已经排好序。
- 函数原型:
template <class ForwardIterator, class T> bool binary_search (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare> bool binary_search (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
#include <algorithm>
#include <iostream>
using namespace std;
void show(int a[], int arraysize)
{
for(int i=0; i<arraysize; ++i)
cout << a[i] << " ";
cout << endl;
}
int main()
{
int a[] = { 1, 5, 8, 9, 6, 7, 3, 4, 2, 0 };
int asize = sizeof(a) / sizeof(a[0]);
cout << "The array is : ";
show(a, asize);
cout << "Let's say we want to search for 2 in the array" << endl;
cout << "So, we first sort the array" << endl;
sort(a, a + asize);
cout << "The array after sorting is : ";
show(a, asize);
cout << "Now, we do the binary search for 2" << endl;
if(binary_search(a, a + 10, 2))
cout << "Element found in the array" << endl;
else
cout << "Element not found in the array" << endl;
cout << "Now, say we want to search for 10" << endl;
if(binary_search(a, a + 10, 10))
cout << "Element found in the array" << endl;
else
cout << "Element not found in the array" << endl;
return 0;
}
- 排序
template <class RandomAccessIterator> void sort (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare> void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
- 逆序
template <class BidirectionalIterator> void reverse (BidirectionalIterator first, BidirectionalIterator last);
- 返回序列中最大值的迭代器
template <class ForwardIterator> ForwardIterator max_element (ForwardIterator first, ForwardIterator last);
template <class ForwardIterator, class Compare> ForwardIterator max_element (ForwardIterator first, ForwardIterator last, Compare comp);
- 返回序列中最小值的迭代器
template <class ForwardIterator> ForwardIterator min_element (ForwardIterator first, ForwardIterator last);
template <class ForwardIterator, class Compare> ForwardIterator min_element (ForwardIterator first, ForwardIterator last, Compare comp);
- 计算序列元素的累加值
template <class InputIterator, class T> T accumulate (InputIterator first, InputIterator last, T init);
template <class InputIterator, class T, class BinaryOperation> T accumulate (InputIterator first, InputIterator last, T init, BinaryOperation binary_op);
#include <algorithm>
#include <iostream>
#include <vector>
#include <numeric> //For accumulate operation
using namespace std;
int main()
{
int arr[] = {10, 20, 5, 23 ,42 , 15};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
cout << "Vector is: ";
for(int i=0; i<n; i++) cout << vect[i] << " ";
sort(vect.begin(), vect.end());
cout << "\nVector after sorting is: ";
for(int i=0; i<n; i++) cout << vect[i] << " ";
reverse(vect.begin(), vect.end());
cout << "\nVector after reversing is: ";
for(int i=0; i<6; i++) cout << vect[i] << " ";
cout << "\nMaximum element of vector is: ";
cout << *max_element(vect.begin(), vect.end());
cout << "\nMinimum element of vector is: ";
cout << *min_element(vect.begin(), vect.end());
cout << "\nThe summation of vector elements is: ";
cout << accumulate(vect.begin(), vect.end(), 0);
cout<< endl;
return 0;
}
- 计算给定元素出现的次数
template <class InputIterator, class T> typename iterator_traits<InputIterator>::difference_type count (InputIterator first, InputIterator last, const T& val);
- 返回指向第一个等于给定元素的指针
template <class InputIterator, class T> InputIterator find (InputIterator first, InputIterator last, const T& val);
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int arr[] = {10, 20, 5, 23 ,42, 20, 15};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
cout << "Occurrences of 20 in vector : ";
cout << count(vect.begin(), vect.end(), 20) << endl;
find(vect.begin(), vect.end(), 5) != vect.end()?
cout << "Element 5 found\n" : cout << "Element 5 not found\n";
return 0;
}
- 二分查找指定元素
template <class ForwardIterator, class T> bool binary_search (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare> bool binary_search (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
- 返回指向第一个不小于指定元素的迭代器(序列有序)
template <class ForwardIterator, class T> ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare> ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
- 返回指向第一个大于指定元素的迭代器(序列有序)
template <class ForwardIterator, class T> ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare> ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int arr[] = {5, 10, 15, 20, 20, 23, 42, 45};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
sort(vect.begin(), vect.end());
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
cout << endl;
auto q = lower_bound(vect.begin(), vect.end(), 20);
cout << "The lower bound for 20 is at position: ";
cout << q-vect.begin() << endl;
auto p = upper_bound(vect.begin(), vect.end(), 20);
cout << "The upper bound for 20 is at position: ";
cout << p-vect.begin() << endl;
return 0;
}
- 过滤连续相等的元素
template <class ForwardIterator> ForwardIterator unique (ForwardIterator first, ForwardIterator last);
template <class ForwardIterator, class BinaryPredicate> ForwardIterator unique (ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int arr[] = {5, 10, 15, 20, 20, 23, 42, 45};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
vect.erase(vect.begin()+1);
cout << "\nVector after erasing the second element: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
sort(vect.begin(), vect.end());
cout << "\nVector before removing duplicate occurrences: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
vect.erase(unique(vect.begin(),vect.end()),vect.end());
cout << "\nVector after deleting duplicates: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
return 0;
}
- 返回下一个置换
template <class BidirectionalIterator> bool next_permutation (BidirectionalIterator first, BidirectionalIterator last);
template <class BidirectionalIterator, class Compare> bool next_permutation (BidirectionalIterator first, BidirectionalIterator last, Compare comp);
- 返回前一个置换
template <class BidirectionalIterator> bool prev_permutation (BidirectionalIterator first, BidirectionalIterator last );
template <class BidirectionalIterator, class Compare> bool prev_permutation (BidirectionalIterator first, BidirectionalIterator last, Compare comp);
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int arr[] = {5, 10, 15, 20, 20, 23, 42, 45};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
cout << "Given Vector is: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
next_permutation(vect.begin(), vect.end());
cout << "\nVector after performing next permutation: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
prev_permutation(vect.begin(), vect.end());
cout << "\nVector after performing prev permutation: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
return 0;
}
- 计算迭代器之间的距离。用于查找下标
- 包含在头文件
iterator
template<class InputIterator> typename iterator_traits<InputIterator>::difference_type distance (InputIterator first, InputIterator last);
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int arr[] = {5, 10, 15, 20, 20, 23, 42, 45};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
cout << "Given Vector is: ";
for_each(vect.begin(), vect.end(), [](int i) {cout << i << " ";});
cout << "\nDistance between first to max element: " << distance(vect.begin(), max_element(vect.begin(), vect.end())) << endl;
return 0;
}
- 以下算法在 C++11 开始支持
- 测试序列是否都满足某个条件
template <class InputIterator, class UnaryPredicate> bool all_of (InputIterator first, InputIterator last, UnaryPredicate pred);
- 测试序列是否存在一个元素满足某个条件
template <class InputIterator, class UnaryPredicate> bool any_of (InputIterator first, InputIterator last, UnaryPredicate pred);
- 测试序列是否都不满足某个条件
template <class InputIterator, class UnaryPredicate> bool none_of (InputIterator first, InputIterator last, UnaryPredicate pred);
- 拷贝序列元素
template <class InputIterator, class Size, class OutputIterator> OutputIterator copy_n (InputIterator first, Size n, OutputIterator result);
- 存储增加的序列
template <class ForwardIterator, class T> void iota (ForwardIterator first, ForwardIterator last, T val);
#include <algorithm>
#include <numeric>
#include <iostream>
using namespace std;
int main()
{
int arr1[] = {1, 2, 3, 4, 5, -6};
all_of(arr1, arr1+6, [](int x) {return x>0;}) ?
cout << "All are positive elments\n" : cout << "Not all are positive elments\n";
any_of(arr1, arr1+6, [](int x) {return x<0;}) ?
cout << "There exists a negative element\n" : cout << "All are positive elments\n";
int arr2[] = {1, 2, 3, 4, 5, 6};
none_of(arr2, arr2+6, [](int x) {return x<0;}) ?
cout << "No negative elements\n" : cout << "There exists a negative element\n";
int arrc[6];
copy_n(arr2, 6, arrc);
cout << "Copyed array: ";
for_each(arrc, arrc+6, [](int i) {cout << i << " ";});
cout << endl;
int arr3[6] = {0};
iota(arr3, arr3+6, 20);
cout << "Assigned array: ";
for_each(arr3, arr3+6, [](int i) {cout << i << " ";});
cout << endl;
return 0;
}
- 根据条件重排序列,返回第一个不满足条件的迭代器
template <class ForwardIterator, class UnaryPredicate> ForwardIterator partition (ForwardIterator first, ForwardIterator last, UnaryPredicate pred);
- 根据条件重排序列,且两组元素内部的相对顺序保持不变。一般是用临时缓冲区实现
template <class BidirectionalIterator, class UnaryPredicate> BidirectionalIterator stable_partition (BidirectionalIterator first, BidirectionalIterator last, UnaryPredicate pred);
- 判断序列是否是根据条件划分的
template <class InputIterator, class UnaryPredicate> bool is_partitioned (InputIterator first, InputIterator last, UnaryPredicate pred);
- 输入队列已经是分割过的,二分查找分界点
template <class ForwardIterator, class UnaryPredicate> ForwardIterator partition_point (ForwardIterator first, ForwardIterator last, UnaryPredicate pred);
- 输入序列中满足条件和不满足条件的分别拷贝到两个序列中
template <class InputIterator, class OutputIterator1, class OutputIterator2, class UnaryPredicate pred> pair<OutputIterator1,OutputIterator2> partition_copy (InputIterator first, InputIterator last, OutputIterator1 result_true, OutputIterator2 result_false, UnaryPredicate pred);
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> vect1 = { 2, 1, 5, 6, 8, 7 };
cout << "The vector is: ";
for_each(vect1.begin(), vect1.end(), [](int i) {cout << i << " ";});
is_partitioned(vect1.begin(), vect1.end(), [](int i) {return i%2==0;}) ?
cout << "\nVector is partitioned" : cout << "\nVector is not partitioned";
partition(vect1.begin(), vect1.end(), [](int i){return i%2==0;});
cout << "\nThe partitioned vector is: ";
for_each(vect1.begin(), vect1.end(), [](int i) {cout << i << " ";});
is_partitioned(vect1.begin(), vect1.end(), [](int i) {return i%2==0;}) ?
cout << "\nNow, vector is partitioned after partition operation":
cout << "\nVector is still not partitioned after partition operation";
vector<int> vect2 = { 2, 1, 5, 6, 8, 7 };
cout << "\n\nThe vector is: ";
for_each(vect2.begin(), vect2.end(), [](int i) {cout << i << " ";});
stable_partition(vect2.begin(), vect2.end(), [](int i) {return i%2==0;});
cout << "\nThe stable partitioned vector is: ";
for_each(vect2.begin(), vect2.end(), [](int i) {cout << i << " ";});
auto it = partition_point(vect2.begin(), vect2.end(), [](int i) {return i%2==0;});
cout << "\nBefore the partition point: ";
for_each(vect2.begin(), it, [](int i) {cout << i << " ";});
cout << "\nAfter the partition point: ";
for_each(it, vect2.end(), [](int i) {cout << i << " ";});
vector<int> vect3 = { 2, 1, 5, 6, 8, 7 };
cout << "\n\nThe vector is: ";
for_each(vect3.begin(), vect3.end(), [](int i) {cout << i << " ";});
vector<int> vecteven, vectodd;
int n = count_if(vect3.begin(), vect3.end(), [](int i) {return i%2==0;});
vecteven.resize(n);
vectodd.resize(vect3.size()-n);
partition_copy(vect3.begin(), vect3.end(), vecteven.begin(),
vectodd.begin(), [](int i) {return i%2==0;});
cout << "\nThe elements that return true for condition are : ";
for_each(vecteven.begin(), vecteven.end(), [](int i) {cout << i << " ";});
cout << "\nThe elements that return false for condition are : ";
for_each(vectodd.begin(), vectodd.end(), [](int i) {cout << i << " ";});
cout << endl;
return 0;
}
- valarray 类:C++98 引入的特殊容器,用于保存和提供对 array 的高效算术操作
- 应用操作到所有的元素,返回一个新的 valarray
valarray apply (T func(T)) const;
valarray apply (T func(const T&)) const;
- 返回所有元素的和
- 返回元素的最小值
- 返回元素的最大值
- 将 valarray 的元素移位,返回新的 valarray。如果参数为正数,左移;否则右移
valarray shift (int n) const;
- 将 valarray 的元素循环移位,返回新的 valarray。如果参数为正数,循环左移;否则循环右移
valarray cshift (int n) const;
- 和另外一个 valarray 交换
void swap (valarray& x) noexcept;
#include <valarray>
#include <iostream>
using namespace std;
int main()
{
valarray<int> varr1 = { 10, 2, 20, 1, 30 };
cout << "The varr1 is: ";
for_each(begin(varr1), end(varr1), [](int i) {cout << i << " ";});
cout << "\nThe sum of varr1 is: " << varr1.sum();
cout << "\nThe max of varr1 is: " << varr1.max();
cout << "\nThe min of varr1 is: " << varr1.min();
valarray<int> varr2;
varr2 = varr1.apply([](int i){return i=i+5;});
cout << "\nThe varr2 (varr1 add 5 for each element) is: ";
for_each(begin(varr2), end(varr2), [](int i) {cout << i << " ";});
valarray<int> varr3;
varr3 = varr1.shift(2);
cout << "\nThe varr3 (varr1 shift 2) is: ";
for_each(begin(varr3), end(varr3), [](int i) {cout << i << " ";});
varr3 = varr1.shift(-2);
cout << "\nThe varr3 (varr1 shift -2) is: ";
for_each(begin(varr3), end(varr3), [](int i) {cout << i << " ";});
varr3 = varr1.cshift(2);
cout << "\nThe varr3 (varr1 cshift 2) is: ";
for_each(begin(varr3), end(varr3), [](int i) {cout << i << " ";});
varr3 = varr1.cshift(-2);
cout << "\nThe varr3 (varr1 cshift -2) is: ";
for_each(begin(varr3), end(varr3), [](int i) {cout << i << " ";});
valarray<int> varr4 = {2, 4, 6, 8};
cout << "\nThe varr4 is: ";
for_each(begin(varr4), end(varr4), [](int i) {cout << i << " ";});
varr1.swap(varr4);
cout << "\nThe varr4 after swap with varr1 is: ";
for_each(begin(varr4), end(varr4), [](int i) {cout << i << " ";});
cout << "\n";
return 0;
}
- 容器是一个对象,保存了其他对象或对象元素的集合
- 容器自己管理元素的存储空间,并且提供成员函数来访问元素,直接访问或通过迭代器访问
- 容器类模板:包括顺序容器、容器适配器、关联容器和无序关联容器
- C++11 引入,替换 C 风格数组。相比 C 风格数组的优点包括
- array 知道自己的大小,因此传递参数时不需要单独传递 array 的大小
- C 风格的数组会有退化成指针的风险,但是 array 不会
- 相比 C 风格数组,array 更加高效、轻量和可靠
- 方法
at
:
get
:不是 array 的类成员函数,而是重载 tuple 类的函数
[]
: 类似于 C 风格的数组访问
front/back
:返回第一个/最后一个元素
size/max_size
:返回 array 的元素数目/可以承载的最大元素数目。二者返回值相同
swap
:和另外一个 array 交换元素
empty
:array 的大小是否是 0
fill
:使用指定值填充正哥 array
- 固定大小数组,顺序连续存储,可使用偏移量访问
- 大小为 0 是有效的,但是不能间接引用,比如 front,back,data
- 交换是按顺序交换每个元素,效率低
- 可以当做 tuple(可以存储不同类型的元素的集合),重载了 get 接口等
- 访问快,可使用偏移量访问,常数时间
- 大小可变数组,顺序连续存储,可使用偏移量访问
- 一开始分配额外的存储空间,容量一般不等于实际大小
- 使用动态分配数组存储元素,插入元素时可能需要重新分配数组,将所有元素移到新的数组,效率低
- 访问快,和 array 一样,在尾部插入和删除也快。删除元素是常数时间,不会重新调整大小
- 在其他位置插入和删除低效,需要线性时间。没有随机访问迭代器
- 双端队列,顺序存储,可在两端增加或减小大小
- 可用随机访问迭代器直接访问单个元素
- vs vector
- 存储可以是不连续的块,在容器增加或减小时内存分配效率更高
- C++11 引入
- 顺序存储,在任意位置插入和删除都是常数时间
- 单向链表,存储位置可以是不同的没有关系的
- vs array/vector/deque
- list 和 forward_list 的插入、删除更有效,对于排序算法也更快(交换更快)
- list 和 forward_list 没有根据位置直接访问元素的方法,同时每个节点需要额外的存储存储链接的相关信息
- list 和 forward_list 遍历较慢
- list 和 forward_list 没有 size 方法,因为很耗时,可以使用 distance 算法(包含在头文件
<iterator>
)计算 begin 和 end 之间的距离,消耗时间是线性的
- 双向链表
- forward_list vs list: 前者只存储一个指向后面对象的链接,后者存储两个链接分别指向前一个和后一个对象,因此两个方向的迭代都比较搞笑,但同时每个节点需要额外的存储,且插入和删除也有额外的时间负载
- 不完全是容器类,而是依赖某一个容器类提供特定的接口,封装之后提供不同于顺序容器的接口
- 后进先出(LIFO),使用标准的容器(vector/deque/list)类模板实现接口,如果初始化未指定容器类,则使用 deque 实现相关接口
- 如
std::stack<int, std::vector<int> > mystack
使用 vector 实现的空的 stack
- 先进先出(FIFO)队列,使用标准的容器(deque/list)类模板实现接口,默认使用 deque
- 如
std::queue<int, std::list<int> > myqueue
使用 list 实现的空的 queue
- 依据严格的弱排序(strict weak ordering)标准第一个元素总是最大的元素,所有元素是非增序的
- 使用标准的容器(vector/deque)类模板实现接口,,默认是 vector
- C++ 默认为 priority_queue 创建最大堆
#include <iostream>
#include <queue>
using namespace std;
void showpq(priority_queue<int> &gq)
{
priority_queue<int> g = gq;
while (!g.empty())
{
cout << " " << g.top();
g.pop();
}
cout << endl;
}
int main ()
{
priority_queue<int> gquiz;
gquiz.push(10);
gquiz.push(30);
gquiz.push(20);
gquiz.push(5);
gquiz.push(1);
cout << "The priority queue gquiz is: ";
showpq(gquiz);
cout << "gquiz.size(): " << gquiz.size() << endl;
cout << "gquiz.top(): " << gquiz.top() << endl;
gquiz.pop();
cout << "after gquiz.pop(): ";
showpq(gquiz);
return 0;
}
- 为 priority_queue 创建最小堆
priority_queue<int, vector<int>, greater<int>> g=gq;
- 下面的语法难记,因此对于数字的值,可以给每个元素乘以 -1,然后使用最大值堆达到最小值堆的效果
#include <iostream>
#include <queue>
using namespace std;
void showpq(priority_queue<int, vector<int>, greater<int>> &gq)
{
priority_queue<int, vector<int>, greater<int>> g = gq;
while(!g.empty())
{
cout << " " << g.top();
g.pop();
}
cout << endl;
}
int main ()
{
priority_queue<int, vector<int>, greater<int>> gquiz;
gquiz.push(10);
gquiz.push(30);
gquiz.push(20);
gquiz.push(5);
gquiz.push(1);
cout << "The priority queue gquiz is: ";
showpq(gquiz);
cout << "gquiz.size(): " << gquiz.size() << endl;
cout << "gquiz.top(): " << gquiz.top() << endl;
gquiz.pop();
cout << "after gquiz.pop(): ";
showpq(gquiz);
return 0;
}
- 实现排好序的数据结构,可以达到快速查询的时间复杂度 O(logn)
- 保存的值都是唯一的,不能修改,只能插入或删除,key 和 value 相同
- 存储的元素总是依照严格的弱排序标准排序,通过内部的比较对象
- 在通过 key 访问单个元素的时候通常比 unordered_set 慢,但是可以访问有序集合的一个子集
- 通常实现为二分搜索树
- 可以存储相同值的元素
- 在通过 key 访问的那个元素的时候比 unordered_multiset 慢
- 关联容器,存储的对象包括一个 key 和映射的 value
- 通过 key 排序和标记唯一元素,存储的元素总是依照严格的弱排序标准排序,通过内部的比较对象
- 在通过 key 访问单个元素的时候通常比 unordered_map 慢,但是可以访问有序集合的一个子集
- 通常实现为二分搜索树