前言
C++,STL算法可分为,非变动算法,变动性算法(重点),移除性算法,变序性算法(重点),排序算法(重点),已序区间算法。所有算法都需要包含头文件<algorithm>。
以下算法typename,均用<int>举例
一、非变动算法
顾名思义,这些算法不会修改容器中的元素。
先看代码,看不懂没关系,谁一开始就懂的呢?看代码下面的详解。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v = {1,2,3,4,5,6,6,6,6,9};
// 循环每个元素并打印出来
for_each(v.begin(), v.end(), [](int a){ cout << a << " "; });
cout << endl;
// 计数,计算6出现的次数
cout << "count = " << count(v.begin(), v.end(), 6) << endl;
// 计数满足条件的元素个数,计算大于等于5的元素个数
cout << "count_if = " << count_if(v.begin(), v.end(), [](int a){ return a >= 5; }) << endl;
// 查找最小元素
auto ite = min_element(v.begin(), v.end());
cout << "min = " << *ite << endl;
// 查找最大元素
ite = max_element(v.begin(), v.end());
cout << "max = " << *ite << endl;
// 查找某个值的第一个出现位置
ite = find(v.begin(), v.end(), 6);
cout << "find = " << *ite << endl;
// 查找满足条件的第一个元素
ite = find_if(v.begin(), v.end(), [](int a){ return a > 3; });
cout << "find_if = " << *ite << endl;
// 查找连续的n个相同元素,查找4个连续的6
ite = search_n(v.begin(), v.end(), 4, 6);
cout << "search_n = " << *ite << endl;
// 查找子序列第一次出现的位置
vector<int> v2 = {6,6};
ite = search(v.begin(), v.end(), v2.begin(), v2.end());
cout << "search = " << *(ite - 1) << endl;
// 查找子序列最后一次出现的位置
vector<int> v3 = {6,6};
ite = find_end(v.begin(), v.end(), v3.begin(), v3.end());
cout << "find_end = " << *(ite + 2) << endl;
// 查找子序列中任一元素在主序列中第一次出现的位置
vector<int> v4 = {6,2};
ite = find_first_of(v.begin(), v.end(), v4.begin(), v4.end());
cout << "find_first_of = " << *ite << endl;
// 查找相邻的两个相等的元素
ite = adjacent_find(v.begin(), v.end());
cout << "adjacent_find = " << *ite << endl;
// 比较两个区间是否相等
string s1 = "hello";
string s2 = "hmllo";
vector<int> vv1 = {1,2};
vector<int> vv2 = {1,2,3};
// cout << equal(s1.begin(), s1.end(), s2.begin()) << endl;
cout << equal(vv1.begin(), vv1.end(), vv2.begin()) << endl;
// 查找两个区间第一次不相等的地方,返回一个pair
auto pair = mismatch(s1.begin(), s1.end(), s2.begin());
std::cout << *pair.first << " " << *pair.second << endl;
return 0;
}
大多数算法,函数的参数通常前两个的参数表示一个区间,表示一个范围。
1. for_each,看单词意思就好理解了,遍历容器的意思。
for_each(v.begin(), v.end(), [](int a){ cout << a << " "; });
for_each(v.begin(), v.end(), [ ](int a){ cout << a << " "; });-----遍历容器元素
- v.begin() -- 开始迭代器
- v.end() -- 结束迭代器
- 最后一个参数可以是用于打印的函数名,或者是使用Lambda表达式用于我们想要输出的内容。
2. count,用于计数
cout << "count = " << count(v.begin(), v.end(), 6) << endl;
count 函数计算容器中等于6的元素个数。
3. count_if,条件计数,满足条件才可以
cout << "count_if = " << count_if(v.begin(), v.end(), [](int a){ return a >= 5; }) << endl;
count_if 函数计算满足Lambda表达式条件(大于等于5)的元素个数。
4. min_element,找最小元素
auto ite = min_element(v.begin(), v.end()); cout << "min = " << *ite << endl;
min_element 函数返回容器中最小元素的迭代器。
5. max_element,找最大元素
ite = max_element(v.begin(), v.end()); cout << "max = " << *ite << endl;
max_element 函数返回容器中最大元素的迭代器。
6. find,查找某个元素
ite = find(v.begin(), v.end(), 6); cout << "find = " << *ite << endl;
find 函数返回容器中第一个等于6的元素的迭代器。
想一想,如何删除相同元素的值呢?
//删除相同元素的值 ite = find(v.begin(),v.end(),6); while(ite != v.end()) { v.erase(ite); ite = find(v.begin(),v.end(),6); } for_each(v.begin(),v.end(),[](int a){ cout<<a<<" "; } ); cout<<endl;
删除相同元素的值
7. find_if,条件查找,满足条件的才可以
ite = find_if(v.begin(), v.end(), [](int a){ return a > 3; }); cout << "find_if = " << *ite << endl;
find_if 返回容器中第一个满足Lambda表达式条件(大于3)的元素的迭代器。
8. search_n,找连续元素,查找连续count个value,返回第一个元素的迭代器。
ite = search_n(v.begin(), v.end(), 4, 6); cout << "search_n = " << *ite << endl;
search_n 查找连续出现4个6的子序列,并返回其起始位置的迭代器。
9. search,查找子序列
v2
在v
中的第一次出现位置,返回其起始位置的迭代器。vector<int> v2 = {6,6}; ite = search(v.begin(), v.end(), v2.begin(), v2.end()); cout << "search = " << *(ite - 1) << endl;
search 函数返回第二个区间在第一个区间第一次出现的位置。
10. find_end 查找子序列
v3
在v
中的最后一次出现位置,返回其起始位置的迭代器。vector<int> v3 = {6,6}; ite = find_end(v.begin(), v.end(), v3.begin(), v3.end()); cout << "find_end = " << *(ite + 2) << endl;
find_if 函数返回第二个区间在第一个区间最后一次出现的位置。
11. find_first_of,函数查找子序列
v4
中任一元素在v
中的第一次出现位置,返回其迭代器。vector<int> v4 = {6,2}; ite = find_first_of(v.begin(), v.end(), v4.begin(), v4.end()); cout << "find_first_of = " << *ite << endl;
find_first_of 函数返回第二个区间里的任一元素在第一个区间里第一次出现的位置
12. adjacent_find,查找相邻元素
ite = adjacent_find(v.begin(), v.end()); cout << "adjacent_find = " << *ite << endl;
adjacent_find 函数查找两个相邻且相等的元素,返回第一个元素的迭代器。
13. equal,比较两个区间是否相等,返回布尔值。
string s1 = "hello"; string s2 = "hmllo"; vector<int> vv1 = {1,2}; vector<int> vv2 = {1,2,3}; cout<<equal(s1.begin(),s1.end(),s2.begin())<<endl;// false cout<<equal(vv1.begin(),vv1.end(),vv2.begin())<<endl; // true
equal 函数比较两个区间是否相等,返回布尔值。注意,如果后者区间前n个元素包括前者区间,就是前n个元素相同,这两个区间也相等。
14. mismatch,快速定位两个区间不一样的地方
auto pair = mismatch(s1.begin(), s1.end(), s2.begin()); std::cout << *pair.first << " " << *pair.second << endl;
mismatch 函数查找两个区间第一次不相等的位置,返回一个
pair
,分别指向两个区间不相等的元素。
到这里,基本的非变动算法就结束啦,如果还有生疏,可以动手运行此代码,可以更好的帮助理解。
二、变动性算法
这些算法会修改容器中的元素,但不会改变容器大小。
老规矩,先看代码。
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
void Print(int a)
{
cout << a << " ";
}
int main()
{
vector<int> v = {1, 2, 3, 4, 5, 6};
// for_each 遍历并修改元素
for_each(v.begin(), v.end(), [](int &a){ a++; cout << a << " "; });
cout << endl;
for_each(v.begin(), v.end(), Print);
cout << endl;
// 拷贝,必须要预留好空间,从头开始拷贝
list<int> l;
l.resize(v.size()); //扩容
// copy 将元素从 v 拷贝到 l
copy(v.begin(), v.end(), l.begin());
for_each(l.begin(), l.end(), Print);
cout << endl;
// copy_backward 将元素从 v 拷贝到 l,从后向前拷贝
copy_backward(v.begin(), v.end(), l.end());
for_each(l.begin(), l.end(), Print);
cout << endl;
// transform 将 v 中的元素变换后拷贝到 l 中,变动完数据塞到新的区间。
transform(v.begin(), v.end(), l.begin(), [](int a){ return a << 1; });
for_each(l.begin(), l.end(), Print);
cout << endl;
// merge 合并两个区间
list<int> l2 = {11, 12, 13, 14, 15, 16};
l2.resize(v.size());
vector<int> result;
result.resize(12);
merge(v.begin(), v.end(), l2.begin(), l2.end(), result.begin());
for_each(result.begin(), result.end(), Print);
cout << endl;
// 填充
fill(v.begin(), v.end(), 10);
fill_n(v.begin(), 3, 99); // 填充前3个元素
for_each(v.begin(), v.end(), Print);
cout << endl;
// 生成新的值填充,在原区间变动
generate(v.begin(), v.end(), [](){ return 100; });
generate_n(v.begin(), 4, [](){ return 66; }); // 生成前4个元素
for_each(v.begin(), v.end(), Print);
cout << endl;
// 按值修改
replace(v.begin(), v.end(), 66, 77);
replace_if(v.begin(), v.end(), [](int a){ return a % 2 != 0; }, 0);
for_each(v.begin(), v.end(), Print);
cout << endl;
// 在新区间
list<int> ss1;
ss1.resize(v.size());
replace_copy(v.begin(), v.end(), ss1.begin(), 0, 22);
for_each(ss1.begin(), ss1.end(), Print);
cout << endl;
// 条件修改
list<int> ss2;
ss2.resize(v.size());
replace_copy_if(v.begin(), v.end(), ss2.begin(), [](int a){ return a % 2 == 0; }, 33);
for_each(ss2.begin(), ss2.end(), Print);
cout << endl;
return 0;
}
1. for_each,这里的for_each里后面的参数加了引用(&)哦,可以修改元素值哦。
for_each(v.begin(), v.end(), [](int &a){ a++; cout << a << " "; });
使用for_each遍历vector容器中的每个元素,对其进行递增操作,并打印出来。
2. copy 拷贝,从头开始拷贝
// 拷贝,必须要预留好空间,从头开始拷贝 list<int> l; l.resize(v.size()); //扩容 copy(v.begin(), v.end(), l.begin());
将vector中的元素拷贝到list中。需要预先调整
list
的大小以容纳所有元素。
3. copy_backward,从后往前拷贝
copy_backward(v.begin(), v.end(), l.end());
同样,拷贝前先预留目标容器的空间大小,保证数据完整性。
4. transform,重点!!!,俗称:操作,变动
transform(v.begin(), v.end(), l.begin(), [](int a){ return a << 1; });
对指定范围内的每个元素执行给定的操作,并将结果存储到目标范围。这里的操作就是,对
vector
中的每个元素进行变换(左移一位),并将结果存储到list
中。
5. merge,合并两个已序区间,到新的区间中。
merge(v.begin(), v.end(), l2.begin(), l2.end(), result.begin());
将两个已排序的区间合并到一个新的区间中。
6. fill 和 fill_n,填充,填充n个
fill(v.begin(), v.end(), 10); fill_n(v.begin(), 3, 99);
fill
将vector
中的所有元素填充为10,fill_n
将前3个元素填充为99。
fill
填充范围内所有,fill_n
填充前n
个
7. generate 和 generate_n,填充,用法与transform相似
generate(v.begin(), v.end(), [](){ return 100; }); generate_n(v.begin(), 4, [](){ return 66; });
generate
使用Lambda表达式生成新的值填充vector
,generate_n
仅填充前4个元素。
generate
在该区间上变动,变动算法不允许有参数。和transform
相似,transform
变动完塞到新的区间,变动算法有参数。
8. replace 和 replace_if,替换,条件替换
replace(v.begin(), v.end(), 66, 77); replace_if(v.begin(), v.end(), [](int a){ return a % 2 != 0; }, 0);
replace
将vector
中的所有66替换为77,replace_if
将所有奇数替换为0。
replace
按值修改,replace_if
把符合条件的值修改。(在原区间变动)
9. replace_copy 和 replace_copy_if,替换->拷贝
replace_copy(v.begin(), v.end(), ss1.begin(), 0, 22); replace_copy_if(v.begin(), v.end(), ss2.begin(), [](int a){ return a % 2 == 0; }, 33);
replace_copy
将vector
中的所有0替换为22并拷贝到list
,replace_copy_if
将所有偶数替换为33并拷贝到list
。(在新的区间)
replace_copy
按值复制,复制到新的区间,并修改。replace_copy_if
把符合条件的值,复制到新的区间,并修改。和transform
相似
三、移除性算法
不推荐使用
先看代码
#include <iostream>
#include <vector>
#include <algorithm>
// 打印元素的函数
void Print(int a) { std::cout << a << " "; }
int main()
{
std::vector<int> v = {1, 2, 2, 3, 4, 5};
// 计数元素2的个数
int c = std::count(v.begin(), v.end(), 2);
// 移除元素2
auto it = std::remove(v.begin(), v.end(), 2);
// 调整容器大小
v.resize(v.size() - c);
// 打印元素
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
std::cout << v.size() << std::endl;
// 移除毗邻的重复元素
auto unique_end = std::unique(v.begin(), v.end());
v.erase(unique_end, v.end());
// 打印元素
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
return 0;
}
因为,每次删除元素,都需要重新调整容器的大小,所以不推荐使用。
1. unique,移除相邻元素(去重)
auto unique_end = std::unique(v.begin(), v.end()); v.erase(unique_end, v.end());
unique
算法移除毗邻的重复元素,并返回一个指向新逻辑结束位置的迭代器,之后使用erase
调整容器大小。少用!
四、变序性算法
这些算法会改变容器中元素的顺序。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
void Print(int a) { std::cout << a << " "; }
int main()
{
std::vector<int> v = {6, 7, 8, 9, 10};
// 逆序 首尾互换
std::reverse(v.begin(), v.end());
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
// 旋转 逆时针旋转
std::rotate(v.begin(), v.begin() + 2, v.end());
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
// 旋转完复制到新的区间
std::vector<int> v_copy(v.size());
std::rotate_copy(v.begin(), v.begin() + 2, v.end(), v_copy.begin());
std::for_each(v_copy.begin(), v_copy.end(), Print);
std::cout << std::endl;
// 全排列算法 - 下一个全排列
std::next_permutation(v.begin(), v.end());
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
// 全排列算法 - 上一个全排列
std::prev_permutation(v.begin(), v.end());
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
// 随机数和随机打乱
std::srand(std::time(nullptr)); // 设置随机种子
std::random_shuffle(v.begin(), v.end()); // 随机打乱
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
// 分组
std::partition(v.begin(), v.end(), [](int a){ return a % 2 == 0; });
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
// 稳定版本分组
std::stable_partition(v.begin(), v.end(), [](int a){ return a % 2 == 0; });
std::for_each(v.begin(), v.end(), Print);
std::cout << std::endl;
return 0;
}
1. 逆序 (
reverse
),首尾互换std::reverse(v.begin(), v.end());
reverse 函数将向量
v
的元素顺序颠倒。
2. 旋转 (
rotate
)std::rotate(v.begin(), v.begin() + 2, v.end());
中间的参数是旋转中心点,
rotate
函数将向量v
的前两个元素移动到末尾,其他元素前移。旋转完就是,34512,3是之前旋转的旋转中心点,现在序列的头。
3. 旋转并复制 (
rotate_copy
),旋转完复制到新的区间中。std::vector<int> v_copy(v.size()); std::rotate_copy(v.begin(), v.begin() + 2, v.end(), v_copy.begin());
rotate_copy
将旋转后的结果复制到另一个容器v_copy
中。
4. 全排列 (
next_permutation
和prev_permutation
)首先了解什么是全排列,例如:123的全排列就是:123,132,213,231,312,321.
std::next_permutation(v.begin(), v.end()); std::prev_permutation(v.begin(), v.end());
next_permutation
将向量v
变为下一个全排列,prev_permutation
将向量v
变为上一个全排列。
5. 随机数和随机打乱 (
rand
和random_shuffle
),随机打乱std::srand(std::time(nullptr)); std::random_shuffle(v.begin(), v.end());
使用
srand
设置随机种子,然后使用random_shuffle
对向量v
进行随机打乱。
6. 分组 (
partition
和stable_partition
)std::partition(v.begin(), v.end(), [](int a){ return a % 2 == 0; }); std::stable_partition(v.begin(), v.end(), [](int a){ return a % 2 == 0; });
partition
函数按照给定条件(偶数)对向量v
进行分组,使得满足条件的元素排在前面。stable_partition
保证了分组后的相对顺序。(稳定的算法:不改变元素的相对位置)
五、排序算法
这些算法用于对容器中的元素进行排序。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void Print(int a)
{
cout << a << " ";
}
int main()
{
vector<int> v = {1,8,3,5,2,6,9,7,10,4};
vector<int> l(10);
// sort 底层实现
// num < 16 插入排序
// num > 16 快排分组
// depth 到底 堆排序
sort(v.begin(), v.end());
// 稳定排序 stable_sort
stable_sort(v.begin(), v.end());
// partial_sort 排序前n个元素有序
partial_sort(v.begin(), v.begin() + 4, v.end());
// partial_sort_copy 排序结果复制到新的区间
partial_sort_copy(v.begin(), v.end(), l.begin(), l.end());
for_each(l.begin(), l.end(), Print);
cout << endl;
// nth_element 根据第n个位置排序
nth_element(v.begin(), v.begin() + 4, v.end());
// partition 改变元素顺序, 符合标准的元素放在前面
partition(v.begin(), v.end(), [](int a) { return a % 2 == 0; });
// stable_partition 稳定版本
stable_partition(v.begin(), v.end(), [](int a) { return a % 2 == 0; });
// make_heap 把一个区间变成堆
make_heap(v.begin(), v.end());
// sort_heap 对堆排序
sort_heap(v.begin(), v.end());
for_each(v.begin(), v.end(), Print);
cout << endl;
return 0;
}
1. sort 排序
sort(v.begin(), v.end());
了解sort底层实现,使用
sort
对向量v
进行排序。sort
函数的底层实现是基于插入排序、快速排序和堆排序的混合算法。元素数量少于16时,使用插入排序;元素数量大于16时,使用快速排序;当递归深度超过某个阈值时,使用堆排序。
2. stable_sort,稳定排序
stable_sort(v.begin(), v.end());
使用
stable_sort
对向量v
进行稳定排序,保证相等元素的相对顺序不变。(归并排序)
3. partial_sort,部分排序
partial_sort(v.begin(), v.begin() + 4, v.end());
对向量
v
的前4个元素进行排序,其余元素不保证有序。
4. partial_sort_copy,部分排序并复制
partial_sort_copy(v.begin(), v.end(), l.begin(), l.end());
将
v
中的元素排序并复制到向量l
中。l
必须有足够的空间存放排序结果。凡是复制到新的区间,必须确保新的区间容量的大小。
5. nth_element,根据第n个位置排序
nth_element(v.begin(), v.begin() + 4, v.end());
对向量
v
进行部分排序,使得第5个元素(从0开始计数)就位,前4个元素小于等于它,后面的元素大于等于它。
6. partition,改变元素顺序
partition(v.begin(), v.end(), [](int a) { return a % 2 == 0; });
重新排列向量
v
,使得符合条件(偶数)的元素在前面。
7. stable_partition,稳定的条件重排
stable_partition(v.begin(), v.end(), [](int a) { return a % 2 == 0; });
稳定地重新排列向量
v
,使得符合条件(偶数)的元素在前面,且保持相对顺序不变。
8. 堆操作
make_heap(v.begin(), v.end()); sort_heap(v.begin(), v.end());
将向量
v
转换为堆结构,并对其进行排序,使其不再是堆,但元素有序
整体说明
sort和stable_sort用于对整个区间进行排序。前者是标准快速排序的实现,后者是稳定排序。
partial_sort和partial_sort_copy用于部分排序,将前n个元素进行排序。
nth_element用于部分排序,使特定位置的元素就位。
partition和stable_partition用于重新排列元素,使符合条件的元素在前面。
make_heap和sort_heap用于将区间转换为堆,并对堆进行排序。