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;
}
posted @ 2022-08-03 16:01  Destiny233  阅读(77)  评论(0)    收藏  举报