内容概要
- 算法适用的容器
- 大部分算法的共性
- 简单讨论C++ 11全部算法的使用
适用容器
In general, we do not use the generic algorithms with the associative containers.
—- 《C++ Primer 5th》
STL中的通用算法主要用于顺序容器,它们包括:
- vector, random access iterator
- deque, random access iterator
- list, bidirectional iterator
- forward_list, forward iterator
- array, random access iterator
- string, random access iterator
顺序容器和算法的桥梁是迭代器,各个类型的迭代器区别主要体现在它们访问的性质,比如是否支持双向访问、随机访问,以及读写的性质,详细可以参见这个网址.
有了这个桥梁,不少算法的参数是容器迭代器,这些参数标示着算法作用区间
In general, the algorithms do not work directly on a container. Instead, they operate by traversing a range of elements bounded by two iterators.
—- 《C++ Primer 5th》
迭代器区间是左闭合右开放的,[begin, end)
算法共性
- 不会改变容器大小,作为输出容器必要要保证已经有那么多个“槽”
- 某些算法对容器存储的元素有重载操作符的要求
- 某些算法不要求容器类型相同,即vector
可以和list 进行操作,得益于迭代器的抽象
C++11的全部算法
《C++ primer 5th》对各类型的算法做了详细的分类总结,以下是对书里内容的提炼
在讨论前,说明下参数意义(它们都是迭代器):
- beg, end: 表示序列中一定范围内的元素
- beg2, end2: 表示另一个序列范围,如果没有end2,那么就假设第二个范围跨度和第一个一样,即[beg2, beg2 + (end - beg));另外beg和beg2的原容器类型不必一致
- dest: 表示目的输入容器,该容器必须足够大以容纳输入
- unaryPred, binaryPred: 分别表示一元和二元的断言函数
- comp: 二元断言函数,用于关联容器中键值的排序
- unaryOp, binaryOp: 表示可调用对象,分别接受一元、二元参数
查找元素的算法
这类函数都有两个重载版本,区别是利用什么来判断相等,第一种是使用 == 运算符,第二种使用自定义断言函数unaryPred、binaryPred
简单查找
// 返回第一个匹配元素的迭代器
find(beg, end, val)
// 返回第一个符合(不符合)断言的元素的迭代器
find_if(beg, end, unaryPred)
find_if_not(beg, end, unaryPred)
// 返回计数
count(beg, end, val)
count_if(beg, end, unaryPred)
// 返回布尔值表示a是否全部(任何一个、全不)符合断言
all_of(beg, end, unaryPred)
any_of(beg, end, unaryPred)
none_of(beg, end, unaryPred)
查找重复子序列
// 返回第一组相等的邻接元素的迭代器
// 如[1,2,3,4,4,5,5,6], 返回指向第一个4的迭代器
adjacent_find(beg, end)
adjacent_find(beg, end, binaryPred)
// 和上面类似,不过是相邻的count个元素,等于val
search_n(beg, end, count, val)
search_n(beg, end, count, val, binaryPred)
查找子串
// 在第一个序列中查找是否包含第二个序列,成功返回第一个元素迭代器,失败返回end1
search(beg1, end1, beg2, end2)
search(beg1, end1, beg2, end2, binaryPred)
// 和上面一样,但是成功是返回最后一个元素的迭代器
find_end(beg1, end1, beg2, end2)
find_end(beg1, end1, beg2, end2, binaryPred)
// 只要包含第二个序列中的任何一个,就返回相应的迭代器,失败返回end1
find_first_of(beg1, end1, beg2, end2)
find_first_of(beg1, end1, beg2, end2, binaryPred)
只读(改)算法
要求迭代器至少是input iterator
// 把unaryOp可调用对象作用于范围内的每个元素
for_each(beg, end, unaryOp)
// 比较两个序列中的每个元素,返回一个包含两个迭代器的pair,分别指向第一个不相等的元素
mismatch(beg1, end1, beg2)
mismatch(beg1, end1, beg2, binaryPred)
// 判断两个序列是否相等
equal(beg1, end1, beg2)
equal(beg1, end1, beg2, binaryPred)
二叉查找算法
要求迭代器是forward iterator, 但如果是random-access iterator则速度更加快
要求容器内的容器已经有序, 如果内部元素没有重载 < 运算符,就要提供比较函数
// lower_bound要求 *result >= val
// upper_bound要求 *reault > val
// 失败则返回end
lower_bound(beg, end, val)
lower_bound(beg, end, val, comp)
upper_bound(beg, end, val)
upper_bound(beg, end, val, comp)
// 返回一个pair,包含上面两个函数的结果
equal_range(beg, end, val)
equal_range(beg, end, val, comp)
// 判断序列是否包含等于val的元素并返回布尔值
binary_search(beg, end, val)
binary_search(beg, end, val, comp)
写算法
这类算法的特点是是会往容器内写入新的元素
只写不读
这类算法要求output iterator作为参数指明写的位置,*_n版本表示写的个数,如果是指明[beg, end)则覆盖里面的元素
fill(beg, end, val)
fill_n(dest, cnt, val)
// Gen是一个可调用对象,返回容器对象
generate(beg, end, Gen)
generate_n(dest, cnt, Gen)
读了再写
以下算法要求input iterator
// 复制写入
copy(beg, end, dest)
copy_if(beg, end, dest, unaryPred)
copy_n(beg, n, dest)
// 复制写入后把旧值替换为新值
replace_copy(beg, end, dest, old_val, new_val) replace_copy_if(beg, end, dest, unaryPred, new_val)
// 移动写入
move(beg, end, dest)
// 计算并把结果写入
transform(beg, end, dest, unaryOp)
transform(beg, end, beg2, dest, binaryOp)
// 合并写入,两个源序列都必须有序
merge(beg1, end1, beg2, end2, dest)
merge(beg1, end1, beg2, end2, dest, comp)
以下算法要求 forward iterator
// 个体交换和区域交换
iter_swap(iter1, iter2)
swap_ranges(beg1, end1, beg2)
// 替换算法
replace(beg, end, old_val, new_val)
replace_if(beg, end, unaryPred, new_val)
以下算法要求 bidirectional iterator
// 逆序复制和移动元素,目的地也是从dest倒序往前
copy_backward(beg, end, dest)
move_backward(beg, end, dest)
// 把[beg, mid),[mid, end)合并替换到自己的[beg, end)中
// 要求合并的两个子序列必须有序
inplace_merge(beg, mid, end)
inplace_merge(beg, mid, end, comp)
划分和排序算法
这些算法都会提供“稳定”和“不稳定”版本,由于“稳定”操作要维护原来元素的顺序,所以可能需要更多的空间、时间
划分算法
要求 bidirectional iterator
// 把范围内的元素分成两部分,返回part
// [first, part)符合断言,[part, last)不符合断言
stable_partition(beg, end, unaryPred)
partition(beg, end, unaryPred)
// 判断访问内的元素是否经过断言切分
// 如果执行了上面的两个函数,就会返回真了
is_partitioned(beg, end, unaryPred)
// 在上面的函数为真的情况下利用这个函数可以找出分割点
partition_point(beg, end, unaryPred)
// 符合unaryPred断言的复制进dest1,否则复制进dest2
// 返回pair,包含的迭代器分别指向最后一个被复制元素的后面一位
partition_copy(beg, end, dest1, dest2, unaryPred)
排序算法(目的是有序)
要求 randam-access iterator,每个函数重载一个不使用 < 运算符的版本
// 排序
sort(beg, end)
stable_sort(beg, end)
sort(beg, end, comp)
stable_sort(beg, end, comp)
// 判断是否有序
is_sorted(beg, end)
is_sorted(beg, end, comp)
// 从头找起有序的最大子串
is_sorted_until(beg, end)
is_sorted_until(beg, end, comp)
// 部分排序,有序的结果放在[beg, mid)中
partial_sort(beg, mid, end)
partial_sort(beg, mid, end, comp)
// 拷贝复制结果到目的地
// [destBeg, destEnd)的大小可以小于源,也可以大于等于
partial_sort_copy(beg, end, destBeg, destEnd) partial_sort_copy(beg, end, destBeg, destEnd, comp)
// 类似于快排,nth是一份迭代器,结束后前面的元素小于等于它,后面大于等于它
nth_element(beg, nth, end)
nth_element(beg, nth, end, comp)
重排序算法
要求 forward iterator *_copy的版本不会覆盖原序列,把写过写到新的序列中
// 通用算法有一个特点是不会改变容器的大小,因此此处并不是把元素删除,而是返回一个迭代器end2,在[beg, end2)中不包含val
remove(beg, end, val)
remove_if(beg, end, unaryPred)
remove_copy(beg, end, dest, val)
remove_copy_if(beg, end, dest, unaryPred)
// 同上,“去掉”邻接的相等元素
unique(beg, end)
unique(beg, end, binaryPred)
unique_copy(beg, end, dest)
unique_copy_if(beg, end, dest, binaryPred)
// 旋转序列,旋转结果是 mid -> end -> beg
rotate(beg, mid, end)
rotate_copy(beg, mid, end, dest)
要求 bidirectional iterator
// 翻转序列
reverse(beg, end)
reverse_copy(beg, end, dest)
要求 random-access iterator
// 随机打乱序列,结果总是一样
random_shuffle(beg, end)
// rand是个可调用对象,接收一个正整型参数n,返回[0-n)间的整数,比如ran:
// int ran(int i) { return std::rand()%i;}
// std::srand(unsigned(std::time(0)));
random_shuffle(beg, end, rand)
// 使用标准库中的随机生成器
// unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
// std::default_random_engine(seed)
shuffle(beg, end, Uniform_rand)
全排序算法
要求序列里的元素没有重复
// 判断两个集合的全序是不是相等,其实就是判断里面的元素是否一样(因为考虑全部顺序,因此显得顺序无关了)
is_permutation(beg1, end1, beg2)
is_permutation(beg1, end1, beg2, binaryPred)
// 全序中的下一个序列,返回布尔值表示成功与否
// 如果已经是最后一种排序则返回false
next_permutation(beg, end)
next_permutation(beg, end, comp)
// 全序中的上一种序列
prev_permutation(beg, end)
prev_permutation(beg, end, comp)
集合算法
它令顺序容器表现得和集合容器一样,前提是容器内的元素有序,而且要求源容器是input iterator,输出容器是output iterator
// 判断第一个容器是否包含了第二个容器
includes(beg, end, beg2, end2)
includes(beg, end, beg2, end2, comp)
// 并集
set_union(beg, end, beg2, end2, dest)
set_union(beg, end, beg2, end2, dest, comp)
// 交集
set_intersection(beg, end, beg2, end2, dest) set_intersection(beg, end, beg2, end2, dest, comp)
// 容器1对容器2的差集
set_difference(beg, end, beg2, end2, dest) set_difference(beg, end, beg2, end2, dest, comp)
// 相互的差集
set_symmetric_difference(beg, end, beg2, end2, dest) set_symmetric_difference(beg, end, beg2, end2, dest, comp)
最小值最大值
//这些输入参数是值或者initialize_list
min(val1, val2)
min(val1, val2, comp)
min(init_list)
min(init_list, comp)
max(val1, val2)
max(val1, val2, comp)
max(init_list)
max(init_list, comp)
// 返回的pair中分别放着最小值和最大值
minmax(val1, val2)
minmax(val1, val2, comp)
minmax(init_list)
minmax(init_list, comp)
// 作用于容器,输入要求是input iterator
min_element(beg, end)
min_element(beg, end, comp)
max_element(beg, end)
max_element(beg, end, comp)
minmax_element(beg, end)
minmax_element(beg, end, comp)
// 字典比较
lexicographical_compare(beg1, end1, beg2, end2) lexicographical_compare(beg1, end1, beg2, end2, comp)
数字算法
比较特殊的是它们定义在< numeric >中
输入源要求input iterator,输出要去output iterator
// 累加,要求类型实现 + 操作符,或则提供二元可调用对象
// init是初始值(不是引用参数),函数最终返回结果值
accumulate(beg, end, init)
accumulate(beg, end, init, binaryOp)
// 两个容器内相对应的元素逐个相乘并相加,要求实现 * 和 + 操作符
inner_product(beg1, end1, beg2, init)
inner_product(beg1, end1, beg2, init, mul_binOp1, add_binOp2)
// [1, 2, 3, 4]生成[1, 3, 6, 10],要求实现 + 操作符
partial_sum(beg, end, dest)
partial_sum(beg, end, dest, binaryOp)
// [1, 8, 3, 4]生成[1, 7, -5, 1],要求实现 - 操作符
adjacent_difference(beg, end, dest)
adjacent_difference(beg, end, dest, binaryOp)
// val递增赋值给每个元素
// 如iota(beg, end, 100) -> [100, 101, 102, ...]
iota(beg, end, val)