STL源码剖析(6)算法1
STL中的算法写在这两个头文件中
-
algorithm
-
numeric
这里只列老版本的C++03的算法,新版本的算法可以在cppreference上看看(太多了),上面的内容比书上更清晰,且全面。
所有的算法都是操作在迭代器控制的一个范围之内的。
算法可以分成质变与非质变两种。前者会修改区间中的元素,后者则不会。
学STL的算法,第一步是学接口,要会用。第二步是学里面的大概实现,知道时间空间复杂度。这些了解后就没什么问题了。
数值算法
accumulate
求区间和。有两个版本,其中第二个版本可指定“+”操作。begin和end指定了求和范围,init指定sum初始值,op可指定加法操作。
本质上就是遍历begin到end的元素,然后进行一次op操作op(sum, iter)
accumulate(begin, end, init);
accumulate(begin, end, init, op);
inner_product
算内积,给两个区间,依次计算每个下标下对应元素的乘积,然后求和。可以看出,这是一个高级版本的求和。“+”对应了op1,“*”对应了op2。
内积,必须要求两个区间长度相同,这里虽然没有硬性规定,但是可以看到第二个区间只有一个first2迭代器。默认长度与第一个区间是一样的。
同accumulate,op也是可以省略的。
inner_product(first1, last1, first2, init);
inner_product(first1, last1, first2, init, op1, op2);
partial_sum
前缀和。destination指定了算出来的前缀和保存到哪个数组
partial_sum(begin, end, destination);
accumulate(begin, end, destination, op);
adjacent_difference
差分数组,求每个元素减去建一个元素的值。接口同partial_sum,这俩操作本身也就是互为逆操作。
iota(C++11)
这个就像go(或者说go像c++)里的iota,给定一个初值,后面的每个元素依次对前一个元素+1
iota(first, last, value);
一些例子
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include <functional>
#include <algorithm>
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto sum1 = std::accumulate(v.begin(), v.end(), 0);
std::cout << "accumulate1: " << sum1 << std::endl;
auto innerSum = std::inner_product(v.begin(), v.end(), v.begin(), 0);
std::cout << "inner_product" << innerSum << std::endl;
std::partial_sum(v.begin(), v.end(), v.begin());
std::for_each(v.begin(), v.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
// 这里会输出原始的v,因为上一步已经求了前缀和,所以再求差分的时候,就相当于还原了
std::adjacent_difference(v.begin(), v.end(), v.begin());
std::for_each(v.begin(), v.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
std::iota(v.begin(), v.end(), 10);
std::for_each(v.begin(), v.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
}
基础算法
容易的,注意,这里的算法都很脆弱。用户需要自己保证迭代器不会失效,STL是不会管这些的。
- equal:两个区间是否相等,前提是用户要保证两个区间的长度相等。提供了仿函数接口,来自定义==意义。
- fill:将区间填入某个值
- fill_n:将区间的前n个数填入某个值
- iter_swap:交换两个迭代器所指位置的元素
- lexicographical_compare:字典序比较第一个区间是否小于第二个区间,一般来比较字符串的字典序
- mismatch:比较两个序列第一个不同元素的位置,返回一个pair
- max,min,swap:太简单就不说了
copy和copy_backward
copy复杂在于,到底是利用C++里的复制构造,还是用c里的按字节拷贝memcopy。
copy接口是两个区间,第二个区间只有first,因为要与第一个区间长度保持一致。所以一共只有3个参数。
copy_backward和copy是类似的,只是前者是倒着拷贝的,所以第三个参数通常是一个区间的end,然后被拷贝的和拷贝的一起倒着复制过去。
一些例子
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <numeric>
int main() {
std::string s1 = "asdf";
std::string s2 = "asdf";
auto isEqual = std::equal(s1.begin(), s1.end(), s2.begin());
std::cout << "s1 is equal s2 ?: " << isEqual << std::endl;
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6 };
std::fill(v.begin(), v.end(), -1);
std::cout << "fill test\n";
std::for_each(v.begin(), v.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
// 用户需要自己保证,前n个是存在的。
std::fill_n(v.begin(), 5, 1000);
std::cout << "fill_n test:\n";
std::for_each(v.begin(), v.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
std::vector<int> v1 = { 0, 1, 2, 3, 4, 5, 6,7 };
std::iter_swap(v1.begin() + 1, v1.end() - 2);
std::cout << "iter_swap test:\n";
std::for_each(v1.begin(), v1 .end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
s1 = "asdFa";
s2 = "asdfa";
auto isLess = std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end());
std::cout << "s1 is less than s2 ?: " << isLess << std::endl;
std::vector<int> v2 = { 1, 2, 3, 4, 5 ,6,7 };
std::vector<int> v3 = { 1, 2, 3, 5, 6, 7,9 };
auto res = std::mismatch(v2.begin(), v2.end(), v3.begin(), v3.end());
std::cout << *res.first << " " << *res.second << std::endl;
std::vector<int> fromVector(10);
std::iota(fromVector.begin(), fromVector.end(), 1);
std::vector<int> toVector(10);
std::copy(fromVector.begin(), fromVector.end(), toVector.begin());
std::cout << "copy test:\n";
std::for_each(toVector.begin(), toVector.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
toVector.clear();
toVector.resize(10);
std::copy_backward(fromVector.begin(), fromVector.end(), toVector.end());
std::cout << "copy_backward test:\n";
std::for_each(toVector.begin(), toVector.end(), [](const int& val) {std::cout << val << " "; });
std::cout << std::endl;
}

浙公网安备 33010602011771号