跳转至

[C++ Basic] Exploring Useful Built-in Functions

导言

简单记录一下遇到过的C++的常用/好用函数

技巧

同时遍历多个容器的[index,value]

for循环实现 对A和B容器的遍历。

for (const auto &I : enumerate(zip(A, B))) {
    std::get<0>(I.value()); //获得第一个容器
    std::get<1>(I.value()); //获得第二个容器
    I.index()               //当前Index
}

循环

  1. v.at(index),相对[INDEX]会处理越界异常
  2. size_t
  3. for_each()
  4. forrange - const auto &node:v

类型转换

string char * char int float double
string x 直接赋值 ... to_string() to_string() to_string()
char * c_str() x ... std::to_string(someInt).c_str() ...
char ... ... x '0'+i ...
int stoi() atoi() int(ch) - 48; int(ch-'0') x ...
float stof() ... ... ... x
double stod() atof() ... ... ... x
atoi()和stoi()的关系
stoi() // string to int
atoi() // string to int
  1. 相同点:
    1. 都是C++的字符处理函数,把数字字符串转换成int输出
    2. 头文件都是#include <cstring>
  2. 不同点:
    1. atoi()的参数是 const char* ,因此对于一个字符串str我们必须调用 c_str()的方法把这个string转换成 const char*类型的,
    2. stoi()的参数是const string*,不需要转化为 const char*
    3. stoi()会做范围检查,默认范围是在int的范围内的,如果超出范围的话则会runtime error!
    4. 而atoi()不会做范围检查,如果超出范围的话,超出上界,则输出上界,超出下界,则输出下界;
to_chars() // c++17最快转换, 类似to_string() 将数字常量转化为char *
#include <cctype>
toupper()

批处理 - 查找判断

基本都是头文件的常用函数,包装常用操作,来简化代码,非必须操作。

all_of;any_of;none_of;

  • 判断数据序列元素(全部、部分,没有)满足判断条件,返回bool类型。
// 检查所有元素是否都是偶数
bool all_even = std::all_of(nums.begin(), nums.end(), [](int n) { return n % 2 == 0; });

find_if

  • std::find_if 是 C++ 标准库中的算法,用于在给定范围内查找满足指定条件的第一个元素。
  • 它遍历范围内的元素,直到找到第一个使谓词返回 true 的元素,然后返回该元素的迭代器。
  • std::find_if 适用于需要根据复杂条件查找某个元素的场景,例如查找符合特定属性的对象或根据条件筛选数据。
template <class InputIt, class UnaryPredicate>
InputIt find_if(InputIt first, InputIt last, UnaryPredicate p);
  • first, last: 输入范围 [first, last)
  • p: 一元谓词,用于检查元素是否满足条件。谓词函数返回 true 表示找到目标元素。

如果找到满足条件的元素,则返回指向该元素的迭代器;否则返回 last

示例
#include <iostream>
#include <vector>
#include <algorithm>

bool isOdd(int i) {
    return i % 2 != 0;
}

int main() {
    std::vector<int> v = {2, 4, 6, 7, 10};

    auto it = std::find_if(v.begin(), v.end(), isOdd);

    if (it != v.end()) {
        std::cout << "First odd number: " << *it << std::endl;
    } else {
        std::cout << "No odd number found" << std::endl;
    }

    return 0;
}

使用 Lambda 表达式:你可以使用 lambda 表达式代替独立的函数:

int main() {
    std::vector<int> v = {2, 4, 6, 7, 10};

    auto it = std::find_if(v.begin(), v.end(), [](int i) { return i % 2 != 0; });

    if (it != v.end()) {
        std::cout << "First odd number: " << *it << std::endl;
    } else {
        std::cout << "No odd number found" << std::endl;
    }

    return 0;
}

equal

//三参数
template< class InputIt1, class InputIt2 >
bool equal( InputIt1 first1, InputIt1 last1,
            InputIt2 first2 );

//四 or 五参数
template< class InputIt1,
          class InputIt2,
          class BinaryPredicate >
constexpr bool equal( InputIt1 first1, InputIt1 last1,
                      InputIt2 first2, InputIt2 last2,
                      BinaryPredicate p );
  • 用 == 运算符来比较两个序列
  • 三个参数版本
    • 前两个参数是第一个序列的开始和结束迭代器,
    • 第三个参数是第二个序列的开始迭代器。
  • 4 个参数:
    • 第一个序列的开始和结束迭代器,第二个序列的开始和结束迭代器,
    • 如果两个序列的长度不同,那么结果总是为 false。
  • 特殊使用: 比较容器内元素是否全部相同
    • equal(cnt.begin() + 1, cnt.end(), cnt.begin());
  • 支持lambda 表达式

    std::equal (std::begin (r1) , std::end (r1) , std::begin (r2),[](const string& s1, const string& s2) { return s1[0] = s2[0]; })
    

max_element()

#include <algorithm>
// C++ vector 容器裡使用 std::max_element 找最大值(或者min_element)的範例,std::max_element 會回傳一個迭代器,這個迭代器會指向該容器範圍內最大值的元素,
vector<int>::iterator result = std::max_element(v.begin(), v.end());
int index = result - v.begin();
int value = (*result)

double max = *max_element(vector.begin(), vector.end());
cout<<"Max value: "<<max<<endl;

upper_bound()

常见于有序序列的二分查找函数
  • 如果查找失败,迭代器的指向和 last 迭代器相同。
//查找[first, last)区域中第一个大于 val 的元素,如果找不到此元素,则返回last。前一个元素是小于等于val的最后一个元素
//comp默认为<,即前面的元素满足!(value<element),直到遇到第一个value<element的元素,返回指向element的迭代器。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//查找[first, last)区域中第一个不符合 comp 规则的元素
//comp 用于自定义比较规则,此参数可以接收一个包含 2 个形参(第一个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。
//给定comp函数或lambda表达式,前面的元素满足!comp(value, element)为true,返回第一个!comp(value, element)为false,即第一个comp(value, element)为true的元素迭代器。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

lower_bound()

  • 二分查找指定区域内查找不小于目标值的第一个元素
  • 如果查找失败,迭代器的指向和 last 迭代器相同。
//在 [first, last) 区域内查找不小于 val 的元素,如果找不到此元素,则返回last。前一个元素是小于val的最后一个元素
//comp默认为<,即前面的元素都满足element<value,直到element≥value,返回指向element的迭代器。
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//在 [first, last) 区域内查找第一个不符合 comp 规则的元素
//comp 用于自定义比较规则,此参数可以接收一个包含 2 个形参(第二个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。
//给定comp函数或lambda表达式,前面的元素满足comp(element, value)为true,返回第一个comp(element, value)为false的元素迭代器。
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);
查找刚好大于 key 的下一个元素set::lower_bound(key)
std::set<int> s = {1, 3, 5, 7, 9};

// 查找键为 5 的元素
auto it1 = s.lower_bound(5);
if (it1 != s.end()) {
    std::cout << "Lower bound of 5 is: " << *it1 << std::endl; // 输出 5
} else {
    std::cout << "5 is greater than all elements in the set." << std::endl;
}
C++ STL实现

如下:

template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
    ForwardIterator it;
    iterator_traits<ForwardIterator>::difference_type count, step;
    count = std::distance(first,last);
    while (count>0)
    {
        it = first; step=count/2; std::advance (it,step);
        if (!(val<*it))  // 或者 if (!comp(val,*it)), 对应第二种语法格式
            { first=++it; count-=step+1;  }
        else count=step;
    }
    return first;
}

template <class ForwardIterator, class T>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
    ForwardIterator it;
    iterator_traits<ForwardIterator>::difference_type count, step;
    count = distance(first,last);
    while (count>0)
    {
        it = first; step=count/2; 
        advance (it,step); // it = mid
        if (*it<val) {  //或者 if (comp(*it,val)),对应第 2 种语法格式
            //考虑[mid,last]的后区间
            first=++it;
            count-=step+1;
        }
        else count=step; //考虑[first,mid]的前区间
    }
    return first;
}

std::binary_search 用于在已排序的范围内查找某个值是否存在。如果找到该值,则返回 true,否则返回 false。

std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};

int value = 5;

// 使用 binary_search 查找值 5 是否存在
bool found = std::binary_search(nums.begin(), nums.end(), value);

equal_range()

std::equal_range 用于在已排序的范围内查找值的范围。它返回一对迭代器,分别指向第一个不小于给定值的元素和第一个大于给定值的元素。

 std::vector<int> nums = {1, 2, 2, 3, 3, 3, 4, 5, 5};

int value = 3;

// 使用 equal_range 查找值 3 的范围
auto range = std::equal_range(nums.begin(), nums.end(), value);

//返回一个 std::pair,其中 first 成员是指向第一个不小于 value 的元素的迭代器,second 成员是指向第一个大于 value 的元素的迭代器。
if (range.first != range.second) {
    std::cout << "值 " << value << " 的范围从索引 " << (range.first - nums.begin())
                << " 到 " << (range.second - nums.begin()) << std::endl;
} else {
    std::cout << "值 " << value << " 未找到" << std::endl;
}

批处理 - 修改

for_each()

std::vector<int> nums = {1, 2, 3, 4, 5};

// 使用 Lambda 表达式
std::for_each(nums.begin(), nums.end(), [](int n) {
    std::cout << n << " ";
});

fill 和 fill_n

fill(ForwardIt first, ForwardIt last, const T& value) //fill函数的作用是:将一个区间的元素都赋予val值。函数参数:fill(first,last,val);//first为容器的首迭代器,last为容器的末迭代器,val为将要替换的值。
fill_n(OutputIt first, Size count, const T& value) //fill_n函数的作用是:给你一个起始点,然后再给你一个数值count和val。把从起始点开始依次赋予count个元素val的值。

copy_if + back_inserter

  • 在 C++ 中,std::copy_if 是一个常用的算法,用于将符合特定条件的元素从一个容器复制到另一个容器。
  • 结合 std::back_inserter,它可以动态地将符合条件的元素插入到目标容器的末尾。std::back_inserter 是一个迭代器适配器,用于确保元素被插入到目标容器的末端,而不需要手动管理目标容器的大小。

1. std::copy_if 概述

std::copy_if 的作用是从输入范围中复制那些满足给定谓词条件的元素到输出范围。它的函数签名如下:

template< class InputIt, class OutputIt, class UnaryPredicate >
OutputIt copy_if( InputIt first, InputIt last, OutputIt d_first, UnaryPredicate pred );

  • first, last:输入范围的开始和结束迭代器。
  • d_first:目标容器的开始迭代器(通常配合 std::back_inserter 使用)。
  • pred:一个一元谓词函数或 lambda 表达式,表示元素需要满足的条件。

2. std::back_inserter 概述

std::back_inserter 是一个迭代器适配器,用来将元素插入到容器的末尾。它通过调用容器的 push_back() 来实现动态扩展容器的大小,因此适合用于那些支持 push_back() 的容器,比如 std::vectorstd::dequestd::list

std::back_inserter(Container& c);

使用 std::back_inserter 时不需要事先调整目标容器的大小,因为每次插入都会自动调整。

示例:结合 std::copy_ifstd::back_inserter
#include <iostream>
#include <vector>
#include <algorithm>  // std::copy_if
#include <iterator>   // std::back_inserter

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::vector<int> evens;

    // 使用 copy_if 复制偶数到 evens 中
    std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evens),
                [](int n) { return n % 2 == 0; });  // Lambda 表达式,条件为偶数

    // 输出 evens 中的元素
    for (int n : evens) {
        std::cout << n << " ";
    }

    return 0;
}

示例解析

  • 输入容器numbers 包含从 1 到 10 的整数。
  • 条件:我们使用了一个 lambda 表达式 [](int n) { return n % 2 == 0; },来筛选偶数。
  • 目标容器evens 用来存放筛选出的偶数。由于使用了 std::back_inserter(evens),无需提前调整 evens 的大小。

输出结果:

2 4 6 8 10

  • std::copy_if 复制符合条件的元素。
  • std::back_inserter 可以避免提前设置目标容器大小,并通过 push_back 将元素添加到容器末尾。
  • 二者结合使得代码简洁高效,尤其是在需要筛选并动态插入元素时非常有用。

reduce (C++17 引入)

reduce 是一个用于对一组数据进行归约操作的算法,它将范围内的元素合并为单个值,通常通过加法或乘法等操作。

template<class InputIt, class T, class BinaryOp>
T reduce(InputIt first, InputIt last, T init, BinaryOp binary_op);
  • firstlast: 要归约的数据范围 [first, last)
  • init: 初始值。
  • binary_op: 二元操作函数,用于将两个元素归约为一个。
示例
#include <iostream>
#include <numeric>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    // 将元素求和
    int sum = std::reduce(v.begin(), v.end(), 0);
    std::cout << "Sum: " << sum << std::endl;

    // 将元素相乘
    int product = std::reduce(v.begin(), v.end(), 1, std::multiplies<>());
    std::cout << "Product: " << product << std::endl;

    return 0;
}

输出:

Sum: 15
Product: 120

transform

transform 是一个变换算法,用于将一个/两个范围内的元素通过某个函数变换为另一个值,可以将结果保存到原范围或另一个容器中。

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first, InputIt last, OutputIt d_first, UnaryOperation unary_op);
  • firstlast: 输入范围 [first, last)
  • d_first: 输出的起始迭代器,结果会写入此位置。
  • unary_op: 一元操作函数,用于将每个元素进行变换。
每个元素乘以2
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::vector<int> result(v.size());

    // 每个元素乘以2
    std::transform(v.begin(), v.end(), result.begin(), [](int i) { return i * 2; });

    for (int i : result) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

2 4 6 8 10
两组vector元素相加
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec1 = {1, 2, 3, 4};
    std::vector<int> vec2 = {5, 6, 7, 8};
    std::vector<int> result(vec1.size());

    // 使用 std::transform 将 vec1 和 vec2 的对应元素相加
    std::transform(vec1.begin(), vec1.end(), vec2.begin(), result.begin(), std::plus<int>());

     // 使用 std::transform 将 vec1 和 vec2 的对应元素相乘
    std::transform(vec1.begin(), vec1.end(), vec2.begin(), result.begin(), [](int a, int b) {
        return a * b;
    });

    // 输出结果
    for (int num : result) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

nth_element & accumulate

  • nth_element 默认的升序排序规则(std::less)时,
  • 从某个序列中找到第 n 小的元素 K,并将 K 移动到序列中第 n 的位置处。
  • 不仅如此,整个序列经过 nth_element() 函数处理后,所有位于 K 之前的元素都比 K 小,所有位于 K 之后的元素都比 K 大。
//排序规则采用默认的升序排序
void nth_element (RandomAccessIterator first,
                  RandomAccessIterator nth,
                  RandomAccessIterator last);
//排序规则为自定义的 comp 排序规则
void nth_element (RandomAccessIterator first,
                  RandomAccessIterator nth,
                  RandomAccessIterator last,
                  Compare comp);
// 其中,各个参数的含义如下:
// first 和 last:都是随机访问迭代器,[first, last) 用于指定该函数的作用范围(即要处理哪些数据);
// nth:也是随机访问迭代器,其功能是令函数查找“第 nth 大”的元素,并将其移动到 nth 指向的位置;
// comp:用于自定义排序规则。

//找到r1中最大的k个元素,并返回它们的和
nth_element(r1.begin(), r1.end() - k, r1.end());
return accumulate(r1.end() - k, r1.end(), 0) //求和之后再加上0。

// accumulte结果溢出问题
//累加和的结果和第三个参数的类型有关
long long sum = accumulate(num.begin(), num.end(), 0LL);  // 需要把初始值设置为long long类型

其他

位运算

按位取反~,按位异或^

在用到位运算的时候用这些函数会更加快捷

  1. __builtin_ffs(x)
    1. 返回 x 的最后一位 1 是从后向前第几位 find first set
  2. __builtin_clz(x)
    1. 返回 x 的二进制下前导的0 的个数,count left zero
  3. __builtin_ctz(x)
    1. 返回 x 的二进制下末尾的0 的个数
  4. __builtin_popcount(x)
    1. 返回 x 的二进制下 1 的个数
  5. __builtin_parity(x)
    1. 返回 x 的二进制下 1 的个数的奇偶性, 偶数个1,输出0; 奇数个1,输出1。

数学概念

//数学
std::__gcd();为内置函数 求最大公因数the greatest common divisor

#include <cmath>
std::round(double_x); //浮点数四舍五入

字符串

//字符串
int sprintf( char *buffer, const char *format [, argument] ... );
eg.sprintf(s, "%d", 123); //把整数123 打印成一个字符串保存在s 中。
  1. 字符串拆分
  2. 数字变成字符串保留小数位

初始化

#include <numeric>
iota(ids, ids + n, 0); //从initial=0 产生连续增量为 1 的值

排序

sort(begin(nums1), end(nums1)); //排序
stable_sort(ids, ids + n, [&](int i, int j) {
    return nums[i] < nums[j]; //构造辅助数组ids,来记录nums数组元素的大小顺序
});

参考文献

评论