C++ 算法

参考资料

说明

  • 截止 C++ 17
  • 有很多重载的函数,仅写最主要的重载
  • 很多算法,并非编译器都有完整的支持

通用概念与基础

  • Function: 函数对象,可以使 lambda 表达式、函数对象,c 风格的函数指针

  • UnaryFunction: 有一个参数的函数

  • BinaryFunction: 有两个参数的函数

  • Predicate: 返回值为 bool 的函数,比如 UnaryPredicate 指的是接受一个参数且返回值为 bool 类型的函数

  • Compare: 一个 BinaryPredicate,接受 2 个参数 T1,T2,返回 T1 是否小于 T2。如果不指定,默认用<比较,需要重载 operator<

  • execution policy: 执行策略

    • 备选项可以是
      • sequenced_policy: 在调用线程中顺序执行算法
      • parallel_policy: 由算法隐式创建线程,并行执行。在每个线程中,算法执行顺序和输入的迭代器顺序一致。比如{1,2,3,4,5}被分到了线程 T1{1,3}和 T2{2,4,5},那么该执行方式保证在 T1 线程中,按照 1->3 的顺序执行;在线程 T2 中,按照 2->4->5 的顺序执行
      • parallel_unsequenced_policy: 和前者相同,但不保证单个线程中的执行顺序
    • 注意
      • 如下算法不一定被编译器支持,甚至很可能不被支持。
      • 除非要处理很多元素,否则并行算法效率并不高于顺序执行
      • 如果并行算法中涉及竞态条件,需要程序员进行同步

常用非修改型算法

用 for_each 进行遍历

原型

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

例子

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
    vector<int> vec{1,2,3,4,5,6,7,8,9,0};
    // transform item of vector to its square
    for_each(vec.begin(), vec.end(), [](int& i){
        i *= i;
    });
    // print items
    for_each(vec.begin(), vec.end(), [](const int& i){
        cout << i << " ";
    });
    cout << endl;
}

for_each 的返回值是你传入的函数对象,因此 for_each 还可以有更高级的用法。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Sum{
public:
    int sum = 0;
    void operator() (const int& i){
        sum += i;
    }
};

int main(){
    vector<int> vec{1,2,3,4,5,6,7,8,9,0};
    Sum sum;
    auto res = for_each(vec.begin(), vec.end(), sum);
    cout << res.sum << endl;
}

相似算法

for_each_n

template< class InputIt, class Size, class UnaryFunction >
InputIt for_each_n( InputIt first, Size n, UnaryFunction f );

all_of、any_of、none_of

  • all_of: Predicate 是否对所有元素为 true
  • any_of: Predicate 是否对至少一个元素为 true
  • none_of: Predicate 是否对所有元素都为 false

例子

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    vector<int> vec{1,2,3,4,5,6,7,8,9,0};
    cout << any_of(vec.begin(), vec.end(), [](const int& i){ return i >= 4;}) << endl;
    cout << all_of(vec.begin(), vec.end(), [](const int& i){ return i >= -1;}) << endl;
    cout << none_of(vec.begin(), vec.end(), [](const int& i){ return i <= -1;}) << endl;
}

mismatch 寻找两个序列的不同

原型

template< class InputIt1, class InputIt2 >
constexpr std::pair<InputIt1,InputIt2>
mismatch( InputIt1 first1, InputIt1 last1,
        InputIt2 first2 );

参数为:第一个序列的首位迭代器,第二个序列的首迭代器
用户需要保证,第二个迭代器长度不小于第一个

例子

#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>

using namespace std;

int main(){
    auto a = vector{1,2,43,5,6,87,9,0};
    decltype(a) b{};
    // copy a to b
    copy(a.begin(), a.end(), back_inserter(b));
    for_each(a.begin(), a.end(), [](const auto& i){cout << i << ' ';}); cout << endl;
    for_each(b.begin(), b.end(), [](const auto& i){cout << i << ' ';}); cout << endl;

    // change value of b at index 4
    b[4] = 9999;
    decltype(a.begin()) a_itr;
    decltype(b.begin()) b_itr;
    tie(a_itr, b_itr) = mismatch(a.begin(), a.end(), b.begin());
    // check the index where two itr mismatch
    cout << "a and b mismatch at index " << distance(a.begin(), a_itr) << endl;
}

find, find_if, find_if_not 以遍历方式查找元素

原型

// 寻找与T value相同的值的位置
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );

// 寻找UnaryPredicate p返回值为True的元素的位置
template< class InputIt, class UnaryPredicate >
InputIt find_if( InputIt first, InputIt last,
                 UnaryPredicate p );
// 寻找UnaryPredicate p返回值为False的元素的位置
template< class InputIt, class UnaryPredicate >
InputIt find_if_not( InputIt first, InputIt last,
                     UnaryPredicate q );

例子

int main(){
    vector<double> a = vector{1.2,2.4,43.5,5.6,6.8,87.9999,9.1,0.445};
    cout << "index of 87.9999 is " << distance(a.begin(), find(a.begin(), a.end(), 87.9999)) << endl;
    cout << "index of first value >=5 is " << distance(a.begin(), find_if(a.begin(), a.end(), [](const double i){return i>=5.0;})) << endl;
}

search:在一个序列中查找另一个序列

原型

template< class ForwardIt1, class ForwardIt2, class BinaryPredicate >
ForwardIt1 search( ForwardIt1 first, ForwardIt1 last,
                   ForwardIt2 s_first, ForwardIt2 s_last,
                   BinaryPredicate p );

例子

int main(){
    string big = "My name is Merry. This is a Message!";
    string small = "this";
    // search ignore letter case
    auto res = search(big.begin(), big.end(), small.begin(), small.end(), [](const char a, const char b){return tolower(a) ==
            tolower(b);});
    if (res == big.end()) cout << "Cannot find substring" << endl;
    else cout << "Find substring at index " << distance(big.begin(), res) << endl;
}

search_n 在序列中寻找连续 n 个相同元素

原型

template< class ForwardIt, class Size, class T >
ForwardIt search_n( ForwardIt first, ForwardIt last, Size count,
                    const T& value );

例子

int main(){
    string big = "10010100100001010110101010110101010101011101010110011000100";
    auto res = search_n(big.begin(), big.end(), 4, '0');
    if (res == big.end()) cout << "Cannot find!" << endl;
    else cout << "Find 0000 at index " << distance(big.begin(), res) << endl;
}

常用修改型算法

"修改型算法"可能是修改原迭代器,也可能是输出一份修改过的值到新的迭代器

copy、copy_if 复制

原型

// 拷贝原迭代器到新的迭代器,程序员应该保证目标迭代器有足够的空间
template< class InputIt, class OutputIt >
OutputIt copy( InputIt first, InputIt last, OutputIt d_first );

// 拷贝UnaryPredicate pred返回true的元素到新的迭代器,程序员应该保证目标迭代器有足够的空间
template< class InputIt, class OutputIt, class UnaryPredicate >
constexpr OutputIt copy_if( InputIt first, InputIt last,
                            OutputIt d_first,
                            UnaryPredicate pred );

例子

int main(){
    string a = "asdasd12345_21asfsbzxvc";
    // copy all string to cout iterator
    copy(a.begin(), a.end(), ostreambuf_iterator<char>(cout));
    cout << endl;
    // copy all letter to cout iterator
    copy_if(a.begin(), a.end(), ostreambuf_iterator<char>(cout), [](const char& c){return isalpha(c);});
}

相似算法

copy_backward

template< class BidirIt1, class BidirIt2 >
BidirIt2 copy_backward( BidirIt1 first, BidirIt1 last, BidirIt2 d_last );

从 last 往前拷贝,将元素拷贝到 d_last(倒着拷贝到新迭代器的末尾)

reverse_copy

template< class BidirIt, class OutputIt >
OutputIt reverse_copy( BidirIt first, BidirIt last, OutputIt d_first );

将迭代器倒序拷贝到新的迭代器

改个方式写吧,这样写好像意义不大

算法 用途
fill(first, last, value) 将迭代器范围内填充值 value
generate(first, last, ) 将迭代器范围内填充 generator 产生的值
remove(first, last, value) 将值为 value 的元素移除
remove_if(fist, last, [](T value){return true;}) 将 UnaryPredicate 返回 true 的元素移除
remove_copy、remove_copy_if 同上,但是将结果写入到新的迭代器,新迭代器应该有足够的内存
replace(first, last, old_value, new_value) 将该范围内等于 old_value 的值替换为 new_value
replace_if(first, last, UnaryPredicate, new_value) 将该范围内 UnaryPredicate 为 true 的值替换为 new_value
replace_copy、replace_copy_if 同上,但是将结果写入到新的迭代器
swap_ranges(first1, last1, first2) 将第一个迭代器和第二个迭代器的值互换
reverse、reverse_copy 将迭代器翻转,第二个函数将结果写入到新的迭代器
rotate、rotate_copy 将迭代器首尾相接形成环,然后向前平移 N 个位置
random_shuffle、shuffle 将迭代器打乱。前者使用 rand()作为函数发生器,后者需要自行指定随机算法
sample(下方例子) 采样,不重复

举例

sample

int main()
{
    std::string in = "hgfedcba", out;
    std::sample(in.begin(), in.end(), std::back_inserter(out),
                5, std::mt19937{std::random_device{}()});
    std::cout << "five random letters out of " << in << " : " << out << '\n';
}

Partition 分区算法

Partition 即将数组分成两部分

算法 说明
partition(first,last, UnaryPredicate) 将 UnaryPredicate 返回为 true 和 false 的值分成两部分,返回第二个分组的首元素的迭代器。分组后,每个组内元素的相对顺序不保证和原来一样
stable_partition 同上,但是分组完成后,保证每个分组内的元素相对顺序和原来一样

sort 排序算法

算法 说明
sort(begin, end, compare) 按照 compare 所给规则,给元素排序
is_sorted(begin, end, compare) 按照 compare 所给规则,判断元素是否有序
is_sorted_until(begin, end) 到哪个迭代器开始,数组无序
partial_sort(begin, end, compare) 数组排好序之后,前 n 个排好序的元素是什么。这会给数组部分排序
stable_sort 同 sort,但是对于相同元素,保证其排序后的相对顺序和原来一致
nth_element(first, nth, last) 数组排好序之后,第 n 个位置上是什么值。运行过后,第 n 个位置上的值,就是数组完全排好序之后的值。该值左边的值都小于它,右边的值都大于它

二分查找

算法 说明
lower_bound 给出有序迭代器中,不小于某元素的第一个值
upper_bound 给出有序迭代器中,大于某个值的第一个元素

其它常用算法

算法 说明
accumulate(first, last, init) 计算累加和
minmax_element 找到最大和最小元素
clamp 将数组中大于某个值 T 的位置填充为 T,小于某个值 t 的位置填充为 t
posted @   ChunhaoXie  阅读(255)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示