C++之STL常用算法(超容易理解)

前言

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);

fillvector中的所有元素填充为10,fill_n将前3个元素填充为99。

fill填充范围内所有,fill_n填充前n

7. generategenerate_n,填充,用法与transform相似

generate(v.begin(), v.end(), [](){ return 100; });
generate_n(v.begin(), 4, [](){ return 66; });

generate使用Lambda表达式生成新的值填充vectorgenerate_n仅填充前4个元素。

generate在该区间上变动,变动算法不允许有参数。和transform相似,transform变动完塞到新的区间,变动算法有参数。

8. replacereplace_if,替换,条件替换

replace(v.begin(), v.end(), 66, 77);
replace_if(v.begin(), v.end(), [](int a){ return a % 2 != 0; }, 0);

replacevector中的所有66替换为77,replace_if将所有奇数替换为0。

replace按值修改,replace_if把符合条件的值修改。(在原区间变动)

9. replace_copyreplace_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_copyvector中的所有0替换为22并拷贝到listreplace_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_permutationprev_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. 随机数和随机打乱 (randrandom_shuffle),随机打乱

std::srand(std::time(nullptr));
std::random_shuffle(v.begin(), v.end());

使用 srand 设置随机种子,然后使用 random_shuffle 对向量 v 进行随机打乱。

6. 分组 (partitionstable_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转换为堆结构,并对其进行排序,使其不再是堆,但元素有序

整体说明

  • sortstable_sort用于对整个区间进行排序。前者是标准快速排序的实现,后者是稳定排序。

  • partial_sortpartial_sort_copy用于部分排序,将前n个元素进行排序。

  • nth_element用于部分排序,使特定位置的元素就位。

  • partitionstable_partition用于重新排列元素,使符合条件的元素在前面。

  • make_heapsort_heap用于将区间转换为堆,并对堆进行排序。

  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值