简介
本文主要介绍 stl_algo.h 中暴露的算法接口。
__medianfor_each(Iter first, Iter last, _Fun f)find(Iter first, Iter last, const _Tp& val)find(Iter first, Iter last const _Tp& val, _Pred pred, tag)find_first_of: 在第一个集合中找第二个集合中任意一个第一次出现的位置find_end: 找第二个集合在第一个集合中最后一次出现的位置,和search对应adjacent_find(ForwardIter first, Forward last): 返回连续相邻相同的迭代器(相同的第一个)- 支持第三个参数是
_BinaryPredicate
- 支持第三个参数是
count(Iter first, Iter last, const _Tp& val)count_if(Iter first, Iter last, _Predicate pred)- 支持两种类型,还有一种是额外增加一个输出参数,
_Size_type& __n
- 支持两种类型,还有一种是额外增加一个输出参数,
search(Iter first1, Iter first2, Iter first2, Iter last2)- 从前面的序列中找后面的序列
search_n(Iter first, Iter last, _Integer _count, const _Tp& val)- 查找连续出现
n次的位置
- 查找连续出现
swap_ranges(Iter first1, Iter last1, Iter first2)- 调用
iter_swap来交换iterator中的内容
- 调用
transform(Iter first, Iter last, Iter output, _Predicate pred)transform(Iter first1, Iter last1, Iter first2, Iter output, _BinaryPredicate pred)generate(Iter first, Iter last, _Generator _gen)generate_n(Iter first, _Size n, _Generator _gen)replace(Iter first, Iter last, const _Tp& old_value, const _Tp& new_value)replace_if(Iter first, Iter last, _Predicate pred, const _Tp& new_value)replace_copy_if(Iter first, Iter last, Iter output, _Predicate pred, const _Tp& new_value)remove_copy(Iter first, Iter last, Iter output, const _Tp& value)- 只复制不同的值
remove_copy_if(Iter first, Iter last, Iter output, _Predicate pred)remove(Iter first, Iter last, const _Tp& value)remove_if(Iter first, Iter last, _Predicate pred)- 去重相关:前提是元素已经是有序的
unique_copy(Iter first, Iter last, Iter output)unique_copy(Iter first, Iter last, _BinaryPredicate _pred)unique(Iter first, Iter last)unique(Iter first, Iter last, _BinaryPredicate _pred)
- 反转: 对于
random_access_iterator会直接<比较,其它使用==reverse(Iter first, Iter last)reverse_copy(Iter first, Iter last, Iter output)
rotate_copyrotate- 默认是进行 left rotate,如果想进行 right rotate,可以利用
rbegin和rend来进行
- 默认是进行 left rotate,如果想进行 right rotate,可以利用
random_shufflerandom_samplerandom_sample_nequal_range: 判断一个集合全是一个数- 二分查找
binary_searchlower_boundupper_bound
inplace_merge- set 相关
includesset_unionset_differenceset_symmetric_differenceset_intersection
partitionstable_partitionpartial_sort:first和middle之间元素有序partial_sort_copysort: 插入和排序和快速排序结合stable_sort: 相同元素位置保持不变max_elementmin_elementnth_element: 第 n 个 iterator 对应位置正确prev_permutationnext_permutationis_heap(Iter first, Iter last)is_heap(Iter first, Iter last, _StrictWeakOrdering _cmp)is_sorted(Iter first, Iter last)is_sorted(Iter first, Iter last, _StrictWeakOrdering _cmp)merge(Iter f1, Iter l1, Iter f2, Iter l2, Iter result)inplace_merge(Iter f, Iter m, Iter l)
一些 notes
find和find_if当tag是random_access_iterator_tag时都采用了unloop的思想, 会把循环拆成 4 的倍数。最后再处理剩余的次数。- 对于
OutputIter和RadomAccessIter两种迭代器,其比较的方式不一样 - 最大公约数算法:
m % n == 0, return n,else old_n = n;n = m % n , m = old_n random_shuffle采用了knuth shuffle原理sort使用插入排序和排序排序结合,当元素少时(16)直接采用插入排序,递归调用最后一层使用partial_sort其原理是 heap sort- 如果实现自定义的
_Compare,_Compare(lhs, rhs)返回true的场景不能包括等于号。因为会影响partition方法对于数组的划分。
- 如果实现自定义的
left and right rotate
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;
template<typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec)
{
for (auto& el : vec)
{
os << el << ' ';
}
return os;
}
int main()
{
vector<int> a{1,2,3,4,5};
vector<int> bb{1,2,3,4,5};
auto b = a.rbegin();
auto m = b;
advance(m, 3);
auto e = a.rend();
rotate(b, m, e);
copy(a.begin(), a.end(), ostream_iterator<int>(cout, ","));
rotate(bb.begin(), bb.begin() + 3, bb.end());
copy(bb.begin(), bb.end(), ostream_iterator<int>(cout, ","));
}
you can test here
sort 原理
sort 这里我们主要讲 4 个东西:
partition- 根据某个准则来将数据进行切分
partial_sort- 利用 heap 来完成排序
sort- 不是稳定的排序,即元素相同,位置可能会发生变化
stable_sort
partition
partition 针对 _ForwardIter 和 _BidirectionalIter 有两种方法
_ForwardIter: 类似于remove中的方法_BidirectionalIter: 采用双端交换的方法
stable_partition 递归调用:
- 当长度小于 buffer size 时,会使用 buffer 来做
partition - 递归调用,
rotate
递归分为 4 段:
- part1
- part2
- part3
- part4
最后把 part2 和 part3 进行交换。
sort
__depth_limit: 递归的深度- 当
__depth_limit = 0时,会直接排序这部分元素
template <class _RandomAccessIter, class _Tp, class _Size>
void __introsort_loop(_RandomAccessIter __first,
_RandomAccessIter __last, _Tp*,
_Size __depth_limit)
{
while (__last - __first > __stl_threshold) {
if (__depth_limit == 0) {
partial_sort(__first, __last, __last);
return;
}
--__depth_limit;
_RandomAccessIter __cut =
__unguarded_partition(__first, __last,
_Tp(__median(*__first,
*(__first + (__last - __first)/2),
*(__last - 1))));
__introsort_loop(__cut, __last, (_Tp*) 0, __depth_limit);
__last = __cut;
}
}
merge 原理
merge 这里主要将 3 个东西:
- merge with buffer
- merge without buffer
- directions of merge: foward and backward
inplace_merge
__merge_without_buffer__merge_adaptive- 和前者的核心逻辑一致,只是在 buffer 可用时,直接调用
merge或者__merge_backward
- 和前者的核心逻辑一致,只是在 buffer 可用时,直接调用
template <class _BidirectionalIter, class _Distance>
void __merge_without_buffer(_BidirectionalIter __first,
_BidirectionalIter __middle,
_BidirectionalIter __last,
_Distance __len1, _Distance __len2) {
if (__len1 == 0 __len2 == 0)
return;
if (__len1 + __len2 == 2) {
if (*__middle < *__first)
iter_swap(__first, __middle);
return;
}
_BidirectionalIter __first_cut = __first;
_BidirectionalIter __second_cut = __middle;
_Distance __len11 = 0;
_Distance __len22 = 0;
if (__len1 > __len2) {
__len11 = __len1 / 2;
advance(__first_cut, __len11);
__second_cut = lower_bound(__middle, __last, *__first_cut);
distance(__middle, __second_cut, __len22);
}
else {
__len22 = __len2 / 2;
advance(__second_cut, __len22);
__first_cut = upper_bound(__first, __middle, *__second_cut);
distance(__first, __first_cut, __len11);
}
_BidirectionalIter __new_middle
= rotate(__first_cut, __middle, __second_cut);
__merge_without_buffer(__first, __first_cut, __new_middle,
__len11, __len22);
__merge_without_buffer(__new_middle, __second_cut, __last, __len1 - __len11,
__len2 - __len22);
}
merge 两端有序的数组:[first, middle), [middle, last)
- 先将前部分拆成两端[first, first_cut), [first_cut, middle)
- 然后将后部分拆成两段,但是根据
first_cut的值来切分:[middle, second_cut), [second_cut, last)
然后将 [first_cut, middle), 与 [middle, second_cut) 进行 rotate。然后递归 merge 前部分和后部分。
本文完