您的当前位置:首页正文

算法常用函数(调用)

2024-11-12 来源:个人技术集锦

数据大小

整数范围

名称十进制二进制内存
(4个0) short int-32,768 ~ 32,767 > 3 ∗ 1 0 4 3*10^4 3104 − 2 15 -2^{15} 215~ 2 15 2^{15} 215-12 Byte
(9个0)int==long-2147483648 ~ 2147483647 > 2 ∗ 1 0 9 2*10^9 2109 − 2 31 -2^{31} 231 ~ 2 31 2^{31} 231-14 Byte
(9个0)unsigned_int== unsigned long0 ~ 4294967295 > 4 ∗ 1 0 9 4*10^9 4109 0 0 0 ~ 2 32 2^{32} 232-14 Byte
(18个0)long long==__int64-9223372036854775808 ~ 9223372036854775807 > 9 ∗ 1 0 18 9*10^{18} 91018 − 2 63 -2^{63} 263 ~ 2 63 2^{63} 263-18 Byte
(19个0)unsigned long long==unsigned __int640 ~ 18446744073709551615 > 1 ∗ 1 0 19 1*10^{19} 11019 0 0 0 ~ 2 64 2^{64} 264-18 Byte

确定是否使用long long数据类型时,只需计算题目中告知的最大取值范围的次数和是否超过8(保险起见,取8而不是9)

杂记

#include<string>头文件

  • string转换为整数、浮点数
    int stoi(const string& _Str, size_t* _Idx = nullptr, int _Base = 10)
    long stol(const string& _Str, size_t* _Idx = nullptr, int _Base = 10)
    float stof(const string& _Str, size_t* _Idx = nullptr)
    double stod(const string& _Str, size_t* _Idx = nullptr)

  • 将整数、浮点数转换为字符串
    string to_string(int _Val)
    to_string(long long _Val)
    string to_string(double _Val)

#include<sstream>头文件

istringstream方法
string sentence 按空格分割,并输入至vector<string> ans

#include<string>
#include<vector>
#include<sstream>
using namespace std;
istringstream in(sentence);
string t; vector<string> ans; 
while (in >> t) 
	ans.push_back(t);

ostringstream 方法,将整数转换为字符串

ostringstream os; //构造一个输出字符串流,流内容为空 
int i = 12; 
os << i; //向输出字符串流中输出int整数i的内容 
cout << os.str() << endl; //利用字符串流的str函数获取流中的内容

STL 中的内置搜索函数

map 与 set

函数原型,set、map都可以

set<T> st; //declaration
st<T> st::iterator it; //iterator declaration
it=st.upper_bound(T key)

返回值:如果键的lower_bound存在于集合中,则迭代器指针指向下限,否则为st.end()

【注意事项:】

  • setmap 自带的 lower_bound , upper_bound,find(类似于binary_search) 的时间复杂度为 O(logn)(这是由于自身结构为红黑树的原理)。

  • 但使用 algorithm 库中的 lower_bound 和 upper_bound 函数对 set 中的元素进行查询,时间复杂度为 0(n)

总结:对于可随机访问的有序容器使用 algorithm 库中的 lower_bound 和 upper_bound 函数时间复杂度为O(logn),

但对于set,multiset,map这种不能随机访问(双向迭代器)的有序容器,要用其自带的 lower_bound 和 upper_bound 的时间复杂度才为 O(logn)。

#include < algorithm >头文件

binary_search返回bool,不是索引):

查找某个元素是否出现
a.函数模板bool binary_search(arr[],arr[]+size , indx)

c.函数功能: 在数组中以二分法检索的方式查找,若在数组(要求数组元素非递减)中查找到indx元素则,若查找不到则返回值为

lower_bound

查找第一个大于或等于目标值的位置。这是可以等于目标值的
a.函数模板
lower_bound(arr[],arr[]+size , indx)

d.举例如下:
  一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111, pos为要插入的位置的下标,则
  /注意因为返回值是一个指针,所以减去数组的指针就是int变量/

vector<int> number{ 4,10,11,30,69,70,96,100 };
auto it = lower_bound(number.begin(), number.begin() + 8, 3) - number.begin();
// it= 0.
// 即number数组的下标为0的位置。

int arr[8] = { 4,10,11,30,69,70,96,100 };
auto pos = lower_bound( arr, arr+ 8, 9) - number; 
// pos = 1,
//即number数组的下标为1的位置(即10所在的位置)。
auto pos = lower_bound( number, number + 8, 111) - number; 
// pos = 8,
//即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。

e.注意
函数lower_bound()firstlast中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的

返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置

upper_bound

代码二分搜索BS的循环与递归实现(返回索引

#include <cstdio>
using namespace std;
int a[100]= {4,10,11,30,69,70,96,100};
int binarySearch(int x,int n)
{
    int left =0;
    int right=n-1;
    while(left<=right)
    {
        int mid =(left+right)/2;
        if(x==a[mid])
        {
            return mid;
        }
        if(x>a[mid])
        {
            left=mid+1;
        }
        else
        {
            right =mid-1;
        }
    }
    return -1;//未找到x
}
//二分搜索递归实现
int recurisonBinarySearch(int left,int right,int x)
{
    if(left>right)
    {
        return -1;
    }
    int mid =(left+right)/2;
    if(x==a[mid])
    {
        return mid;
    }
    if(x>a[mid])
    {
        return recurisonBinarySearch(mid+1,right,x);
    }
    else
    {
        return recurisonBinarySearch(left,mid-1,x);
    }
}
int main()
{
    int x;
    int ans1,ans2;
    scanf("%d",&x);
    ans1=binarySearch(x,8);
    ans2=recurisonBinarySearch(0,7,x);
    printf("%d %d\n",ans1,ans2);
    return 0;
}

#include < numeric >头文件

accumulate()函数介绍

accumulate()函数返回数组内元素的累积值。
例如,可以使用accumulate()函数计算vector<int> nums中所有元素的累加和,通过修改操作符(binary_op)可以计算累乘积或其他自定义累计结果。

(1).函数定义

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

first, last: 容器迭代器,计算范围;

init: 初始值,默认为1。若初始值init=2,那么accumulate(nums.begin(),nums.end(),2) 返回 2+nums[0]+nums[1]+…+nums[n-1];

binary_op: 元素间的操作符号,默认为plus(求和)。

(2).用法示例
a. 基本用法,求vector<int> nums的元素累计和、累计减、累计乘、累计除
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {
    vector<int> nums = { 6,3,2 };
    // 默认求和,输出为:0+6+3+2 = 11
    cout << accumulate(nums.begin(), nums.end(), 0) << endl;
    // 初始值为1求和,输出为:1+6+3+2 = 12
    cout << accumulate(nums.begin(), nums.end(), 1) << endl;
    // 求累减,输出为:0-6-3-2 = -11
    cout << accumulate(nums.begin(), nums.end(), 0, minus<int>()) << endl;
    // 求累乘,输出为:1*6*3*2 = 36
    cout << accumulate(nums.begin(), nums.end(), 1, multiplies<int>()) << endl;
    // 求累除,输出为:36/6/3/2 = 1
    cout << accumulate(nums.begin(), nums.end(), 36, divides<int>()) << endl;
    return 0;

输出:

11
12
-11
36

b. 自定义操作符binary_op
如1.所示,可以设置二元操作符为plus,minus,multipliesdivides对容器内的元素进行加、减、乘和除accumulate操作。

同样的,我们可以使用函数指针、函数对象或lambda表达式自定义一个二元运算符对元素进行accumulate操作。

接下来我们定义一个 求和后再模3 的二元运算符 add_mod_3_fun,即add_mod_3_fun(a,b) = (a+b)%3,然后使用该二元运算符对nums进行accumulate操作。

代码如下:

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

int add_mod_3_fun(const int& a, const int& b) {
    return (a + b) % 3;
}
class ADD_MOD_3 {
public:
    int operator()(const int& a, const int& b) {
        return (a + b) % 3;
    }
};

int main(int, char**) {
    vector<int> nums = { 6,3,2 };

    // 使用自定义二元运算符
    
    // 使用函数指针,输出为 (0+6)%3=0 -> (0+3)%3=0 -> (0+2)%3 = 2
    cout << accumulate(nums.begin(), nums.end(), 0, add_mod_3_fun) << endl;
    // 使用函数对象
    cout << accumulate(nums.begin(), nums.end(), 0, ADD_MOD_3()) << endl;
    // 使用lambda表达式
    cout << accumulate(nums.begin(), nums.end(), 0, [](const int& a, const int& b) {return (a + b) % 3;}) << endl;
    return 0;
}

输出:

2
2
2

需要注意的是,在使用函数指针、函数对象或lambda表达式自定义运算符号时,参数列表应该为const int&类型以保证acumulate()函数不会对数组内原有元素做任何修改,尽管不为const int&类型依旧可以正常运行,但是为了安全考虑还是需要尽量使用const int&

adjacent_difference()函数介绍

从名字也可以看出adjacent_difference()是用来计算数组各元素与相邻元素(这里的相邻指的是前一个元素)之间差异的函数。
例如:若nums为待计算数组,result为存放差异结果的数组,那么:

result[0] = nums[0]
result[1] = nums[1] - nums[0]
result[2] = nums[2] - nums[1]
result[3] = nums[3] - nums[2]
result[4] = nums[4] - nums[3]

可以看到,由于数组首个元素没有前一个元素,函数规定数组首个元素与前一个元素的差异为首个元素本身。另外在计算差异时,默认使用-减法运算,即differenc[i]=nums[i]-nums[i-1]

(1).函数定义

template <class InputIterator, class OutputIterator>
   OutputIterator adjacent_difference (InputIterator first, InputIterator last,OutputIterator result);

template <class InputIterator, class OutputIterator, class BinaryOperation>
   OutputIterator adjacent_difference ( InputIterator first, InputIterator last,OutputIterator result, BinaryOperation binary_op );

first, last:容器迭代器,计算范围;
result:迭代器,存储差异结果数组 起始位置;
binary_op:计算差异的运算符,默认为减法运算;
函数返回值:迭代器,存储差异结果数组的最后一个差异元素的下一个位置;

(2).用法示例
a. 基本用法,求vector<int>的各元素与前一个元素的差、和、积和商

代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {
    vector<int> nums = { 1,2,3,5,9,11,12 };
    cout << "nums:";
    for (int i = 0; i < nums.size(); i++) {
        cout << nums[i] << " ";
    }
    cout << endl;
    vector<int> results(nums.size());
    // 计算各元素与前一个元素的 差
    cout << "minus:";
    vector<int>::iterator iter = adjacent_difference(nums.begin(), nums.end(), results.begin());
    for (int i = 0; i < results.size();i++) {
        cout << results[i] << " ";
    }
    cout << endl;
    // 计算各元素与前一个元素的 和
    cout << "plus:";
    iter = adjacent_difference(nums.begin(), nums.end(), results.begin(), plus<int>());
    for (int i = 0; i < results.size();i++) {
        cout << results[i] << " ";
    }
    cout << endl;
    // 计算各元素与前一个元素的 乘
    cout << "multiplies:";
    iter = adjacent_difference(nums.begin(), nums.end(), results.begin(), multiplies<int>());
    for (int i = 0; i < results.size();i++) {
        cout << results[i] << " ";
    }
    cout << endl;
    // 计算各元素与前一个元素的 除
    cout << "divides:";
    iter = adjacent_difference(nums.begin(), nums.end(), results.begin(), divides<int>());
    for (int i = 0; i < results.size();i++) {
        cout << results[i] << " ";
    }
    cout << endl;
    return 0;
}

结果:

nums:1 2 3 5 9 11 12
minus:1 1 1 2 4 2 1
plus:1 3 5 8 14 20 23
multiplies:1 2 6 15 45 99 132
divides:1 2 1 1 1 1 1

从输出结果中我们可以看出,无论我们使用什么运算符(减、加、乘还是除)计算差异,数组首个元素与前一个元素的差异都为首个元素本身。

b. 自定义操作符binary_op
与accumulate()类似,adjacent_difference()函数中也可以使用函数指针、函数对象或lambda表达式自定义差异运算法则,此处不在赘述。

c. 注意事项

  • 在使用数组result存储nums差异结果时,需要保证result.size()>=nums.size()
  • adjacent_difference()函数的返回值指向的是 最后一个差异结果的下一个元素位置,而不是result数组的最后一个位置。

例如,若nums.size()等于3,result.size()等于5,那么
iter=adjacent_difference(nums.begin(), nums.end(), result.begin());
之后,iter指向result的第4个元素位置。

inner_product()函数介绍

inner product直译成中文为内积,的确该函数的功能与向量计算中的内积运算类似,inner_product()函数可以计算两个数组(向量)的内积结果。

例如:
两个数组分别为nums_1 = {1,2,3},nums_2 = {2,3,4}。那么两数组的内积
// 整数内积,起始值也要为整数

inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(),0);

即为:0 + (1*2)+(2*3)+(3*4) = 20

(1).函数定义

template <class InputIterator1, class InputIterator2, class T>
   T inner_product (InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, T init);

template <class InputIterator1, class InputIterator2, class T,class BinaryOperation1, class BinaryOperation2>
    T inner_product (InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, T init,BinaryOperation1 binary_op1,BinaryOperation2 binary_op2);

first1,last1,first2:容器迭代器,分别代表第一个数组的起始位置、终止位置和第二个数组的起始位置;
init初始值,最后与两数组的内积向加;注意避掉踩坑点,如果要进行浮点数运算,初始值也要设置为浮点数
binary_op1:二元运符,内积操作中外部的运算符号,默认为加法运算;
binary_op2:二元运符,内积操作中内部的运算符号,默认为乘法运算;

(2).用法示例
a. 基本用法,计算两个数组的内积
将数组nums_1,nums_2视作两个相同维度的向量,inner_product()函数可以计算这两个向量(数组)的内积。
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {
    vector<int> nums_1 = { 1,2,3 };
    vector<int> nums_2 = { 2,4,6 };
    // 28 = 0 + (1*2)+(2*4)+(3*6) = 2+8+18 = 28
    cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 0) << endl;
    // 33 = 5 + (1*2)+(2*4)+(3*6) = 2+8+18 = 33
    cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 5) << endl;
    return 0;
}

输出:

28
33
1
2

b. 自定义内积运算法则
默认两个数组的内积运算为:各个元素相乘(内部运算符,binary_op2)然后再相加(binary_op1)。我们可以通过自定义binary_op1binary_op2实现自定义的内积运算。示例如下:
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {
    vector<int> nums_1 = { 1,2,3 };
    vector<int> nums_2 = { 2,4,6 };
    // 自定义内积操作
    // 外部运算依旧为加法,内部运算为 减法
    // -5 = 1 + (1-2)+(2-4)+(3-6) = -5
    cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 1, plus<int>(), minus<int>()) << endl;
    // 外部运算依旧为乘法,内部运算为 加法
    // 162 = 1*(1+2)*(2+4)*(3+6) = 3*6*9 = 162
    cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 1, multiplies<int>(), plus<int>()) << endl;
    return 0;
}

输出:

-5
162

应用
ans向量与com向量,运算结果一致。

void tx2() {
    /* 输入值
    2
    3.24 6.09
    0.4 0.7
    0.5 0.6
    */
    int c;
    cin >> c;
    vector<float> f(c);
    for (int i = 0; i < c; ++i) cin >> f[i];
    vector<vector<float>> w(c, vector<float>(c));
    for (int i = 0; i < c; ++i) {
        for (int j = 0; j < c; ++j) {
            cin >> w[i][j];
        }
    }
    vector<float> ans(c), cpm(c);
    for (int i = 0; i < c; ++i) {
        for (int j = 0; j < c; ++j) {
            ans[i] += w[i][j] * f[j]; // ?
        }
        cpm[i] = inner_product(w[i].begin(), w[i].end(), f.begin(), 0.0); // ?
    }
    auto it = max_element(ans.begin(), ans.end());
    int pos = it - ans.begin();
    cout << pos;
}

partial_sum()函数介绍

partial_sum()函数可以计算数组的部分和,所谓部分和即为数组从首个元素到第i个元素的和。
例如:若nums为待计算数组,result为存放部分和的结果数组,那么:

result[0] = nums[0]
result[1] = nums[0] + nums[1]
result[2] = nums[0] + nums[1] + nums[2]
result[3] = nums[0] + nums[1] + nums[2] + nums[3]
result[4] = nums[0] + nums[1] + nums[2] + nums[3] + nums[4]

其实从以上示例中也可以看出,所谓的部分和即为数组的前缀和。

(1).函数定义

template <class InputIterator, class OutputIterator>
   OutputIterator partial_sum (InputIterator first, InputIterator last,OutputIterator result);

template <class InputIterator, class OutputIterator, class BinaryOperation>
   OutputIterator partial_sum (InputIterator first, InputIterator last,OutputIterator result,BinaryOperation binary_op);

first,last:容器迭代器,待计算的数组起始、终止位置;
result:容器迭代器,存放部分和结果数组的起始位置;
binary_op:二元运算符,计算部分和的运算法则;
返回值:容器迭代器,结果数组最后一个 部分和元素 的下一个位置;

(2).用法示例
a. 基本用法,计算数组的部分和(前缀和)
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {
    vector<int> nums = { 1,2,3,4,5 };
    vector<int> results(nums.size(), 0);

    vector<int>::iterator iter = partial_sum(nums.begin(), nums.end(), results.begin());
    for (int i = 0; i < results.size(); i++) {
        cout << results[i] << " ";
    }
    cout << endl;
    return 0;
}

输出:

1 3 6 10 15

b. 自定义操作符binary_op
与前面accumulate(), adjacent_difference()函数类似,可以使用函数指针,函数对象lambda表达式自定义求部分和的运算符号,此处不在赘述。
需要注意的是:结果容器results和原数组nums内的元素必须相同
例如:假如nums内元素为pair<int, int>类型,我们需要计算nums[i].second的前缀和,那么results数组内的元素也必须为pair<int, int>类型,代码如下:

vector<pair<int, int>> nums;
for(int i=0; i<5; i++){
nums.push_back(pair<int, int>(i, 2*i));
}
vector<pair<int, int>> results(nums.size());
partial_sum(nums.begin(), nums.end(), results.begin(), [](const pair<int, int>&a, const pair<int, int>&b){
return pair<int, int>(a.second+b.second, 0);
});

iota()函数介绍

单从函数名字iota()并不能看出该函数的作用,因为该函数与前面其他函数的命名规则不同,该函数的名字iota并不是源自英文单词,而是来自希腊字母ι。该函数源于Ken Iverson发明的编程语言APL中的ι函数,其作用生成一个以n为起始元素值,之后每个元素都增1,同时希腊字母ι也有极小,很小的一部分的意思。
因此,再C++中iota()函数的作用与APL中的ι函数类似,为使用以指定数值为起始,之后的元素依次增1的序列填充指定数组。

(1).函数定义

template <class ForwardIterator, class T>
  void iota (ForwardIterator first, ForwardIterator last, T val);

first,last:容器迭代器,待填充的数组;
val:初始元素值;

(2).用法示例
填充数组nums,令其第一个元素值为0,步长为1的递增数列。
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {
    vector<int> nums(5, 0);
    iota(nums.begin(), nums.end(), 0);
    for (int i = 0; i < nums.size(); i++) {
        cout << nums[i] << " ";
    }
    return 0;
}

输出:

0 1 2 3 4

(补充)C++17新增的几个实用函数

gcd()函数

gcd()函数用来计算最大公约数(greatest common divisor)。
例如:gcd(12,18)返回6

lcm()函数

lcm()函数用来计算最小公倍数(least common multiple)。
例如:gcd(12,18)返回36

midpoint()函数

midpoint()函数用来计算整数、浮点数或指针的中点值。
例如:midpoint(2,5)返回3
解释:(2+5)/2取整等于3

除了上面这三个函数之外,C++17中还新增了reduce(),transform_reduce(),inclusive_scan()等函数,详细信息读者可以查阅Standard library header 。

参考:

显示全文