C++ STL算法总结

1,756 阅读6分钟

大多重载的算法都同时有两个版本的:

  • 用"=="判断元素相等,或者用"x<y,y<x"比较大小判断相等
  • 多出一个类型参数"Pred"和函数形参"Pred op",通过判断表达式op(x,y)的返回值来判断x==y或者x<y。 比如有两个版本的min_element:
iterator min_element(iterator first, iterator last);
iterator min_element(iterator first, iterator last,Pred op);

STL中的算法大致有以下7种:

需要说明的是,算法名字并不代表实际意义,可以用自己的想象力做算法名字之外的事情;同时有些非常用算法并未列出

1.不变序列算法

该类算法不修改算法所作用的容器或对象

适用于顺序容器和关联容器

时间复杂度都是O(n)

常用算法:

名称 说明(代码为函数模板,InIt代表迭代器,简单的不说明)
find InIt find(InIt first, InIt last, const T& val);
count size_t count(InIt first, InIt last, const T& val);
min
max
min_element FwdIt min_element(FwdIt first, FwdIt last);
max_element FwdIt max_element(FwdIt first, FwdIt last);
search 查找另一区间第一次出现的位置
find_first_of
find_end
equal 判断两区间是否相等
for_each Fun for_each(InIt first, InIt last, Fun f);

2.变值算法

会修改源区间或目标区间元素的值

值被修改的那个区间不可以是关联容器(关联容器会涉及到重新排序)

常用算法:

名称 说明(Init输入迭代器,OutIt输出迭代器)
for_each
copy OutIt copy(InIt first, InIt last, OutIt x);
copy_backward
transform OutIt transform(InIt first, InIt last, OutIt x, Unop uop);
swap_ranges
fill
fill_n
generate
generate_n
replace
replace_if
replace_copy
replace_copy_if

3.删除算法

删除一个容器中的某些元素

删除算法不应作用于关联容器(关联容器会涉及到元素排序)

算法复杂度都是o(n)

删除并不会使元素减少

* 将所有应该被删除的元素看做空位子
* 用留下的元素从后往前移, 依次去填空位子
* 空位置填满后,剩下的位置由刚开始的所在位置元素填(即剩下的位置不变)
名称 说明
remove 删除区间中等于某个值的元素
unique 删除区间中连续相等的元素, 只留下一个(可自定义比较器)
remove_if
remove_copy
remove_copy_if
unique_copy

4.变序算法

变序算法改变容器中元素的顺序,但是不改变元素的值

变序算法不适用于关联容器

算法复杂度都是O(n)的

名称 说明(FwdIt迭代器)
reverse 颠倒区间的前后次序
rotate 将区间进行循环左移
next_permutation 将区间改为下一个排列(可自定义比较器)
prev_permutation 将区间改为上一个排列(可自定义比较器)
random_shuffle 随机打乱区间内元素的顺序
stable_patition 把区间内满足某个条件的元素移到前面,不满足该条件的移到后面,而对这两部分元素, 分别保持它们原来的先后次序不变
partition 把区间内满足某个条件的元素移到前面,不满足该条件的移到后面
rotate_copy 将区间以首尾相接的形式进行旋转后的结果拷贝到另一个区间,源区间不变
reverse_copy 把一个区间颠倒后的结果拷贝到另一个区间,源区间不变

5.排序算法

排序算法需要随机访问迭代器的支持

不适用于关联容器和list

比前面的变序算法复杂度更高, 一般是O(nlog(n))

名称 说明(RanIt迭代器)
sort void sort(RanIt first, RanIt last, Pred pr)
stable_sort
partial_sort
partial_sort_copy
nth_element
make_heap
push_heap
pop_heap
sort_heap

sort 实际上是快速排序, 时间复杂度 O(n*log(n))

  • 平均性能最优
  • 但是最坏的情况下, 性能可能非常差

如果要保证 “最坏情况下” 的性能, 那么可以使用stable_sort

  • stable_sort 实际上是归并排序, 特点是能保持相等元素之间的先后次序
  • 在有足够存储空间的情况下, 复杂度为 n * log(n), 否则复杂度为n * log(n) * log(n)
  • stable_sort 用法和 sort相同。

排序算法要求随机存取迭代器的支持, 所以list不能使用排序算法, 要使用list自带的sort成员函数list::sort。

6.有序区间算法

要求所操作的区间是已经从小到大排好序的

需要随机访问迭代器的支持

有序区间算法不能用于关联容器和list

名称 说明(前6个很常用)
binary_search 判断区间中是否包含某个元素(折半查找)
includes 判断是否一个区间中的每个元素,都在另一个区间中
lower_bound 查找最后一个不小于某值的元素的位置
upper_bound 查找第一个大于某值的元素的位置
equal_range 同时获取lower_bound和upper_bound
merge 合并两个有序区间到第三个区间
set_union 将两个有序区间的并拷贝到第三个区间
set_intersection 将两个有序区间的交拷贝到第三个区间
set_difference 将两个有序区间的差拷贝到第三个区间
set_symmetric_difference 将两个有序区间的对称差拷贝到第三个区间
inplace_merge 将两个连续的有序区间原地合并为一个有序区间

bitset

bitset一个实际上不属于STL算法,但是会很常用。

template<size_t N>
class bitset{
    ...
}
bitset<40>bst; #bst是一个由40位组成的对象,用bitset的函数可以方便地访问任何一位

bitset的成员函数:

bitset<N>& operator&=(const bitset<N>& rhs);
bitset<N>& operator|=(const bitset<N>& rhs);
bitset<N>& operator^=(const bitset<N>& rhs);
bitset<N>& operator<<=(size_t num);
bitset<N>& operator>>=(size_t num);
bitset<N>& set(); //全部设成1
bitset<N>& set(size_t pos, bool val = true); //设置某位
bitset<N>& reset(); //全部设成0
bitset<N>& reset(size_t pos); //某位设成0
bitset<N>& flip(); //全部翻转
bitset<N>& flip(size_t pos); //翻转某位 
reference operator[](size_t pos); //返回对某位的引用
bool operator[](size_t pos) const; //判断某位是否为1
reference at(size_t pos);
bool at(size_t pos) const;
unsigned long to_ulong() const; //转换成整数
string to_string() const; //转换成字符串
size_t count() const; //计算1的个数
size_t size() const;
bool operator==(const bitset<N>& rhs) const;
bool operator!=(const bitset<N>& rhs) const; 
bool test(size_t pos) const; //测试某位是否为 1
bool any() const; //是否有某位为1
bool none() const; //是否全部为0
bitset<N> operator<<(size_t pos) const;
bitset<N> operator>>(size_t pos) const;
bitset<N> operator~();
static const size_t bitset_size = N;
#注意: 第0位在最右边

总结起来呢,就是实现容器或对象元素的查删改比较排序