STL算法

概述

STL算法都被设计用来处理一个或者多个iterator区间,一般第一个区间提供起点和重点,第二个区间提供起点即可。
某些STL算法允许调用者传递自定义操作,这些操作可以是普通函数,函数对象,或者lamda表达式。比如,find_if算法允许第三个参数传递一个自定义操作,查找满足此操作(即结果返回true)的那个元素;有比如min_element允许传入第三个参数定义自己的比较规则。

分类

有些算法是只读的,有些会改变元素自身,还有则改动元素顺序,一般可将其分为以下几类:

  1. 不变序列算法
  2. 变值算法
  3. 删除算法
  4. 变序算法
  5. 排序算法
  6. 有序区间算法
  7. 数值算法
    其中某些算法可能属于多个类,则将其放入最贴切的分类中。

1.不变序列算法

此类算法不会修改所作用的容器或对象,适于所有容器。它们的时间复杂度都是 O(n)的。

名称效果
min求两个对象中较小的 (可自定义比较器 可自定义比较器 )
max求两个对象中较大的 (可自定义比较器 可自定义比较器 )
min_element求区间中的最小值 (可自定义比较器 )
max_element求区间中的最大值 (可自定义比较器 )
for_each对区间每个元素做某种操作
count计算区间中等于某值的元素个数
count_if计算区间中符合某种条件的元素个数 计算区间中符合某种条件的元素个数
find在区间中查找等于某值的元素
find_if在区间中查找符合某条件的元素
find_end在区间中查找另一个区间最后一次出现的位置 (可自定义比较器 )
find_first_of在区间中查找第一个出现在另一个区间中的元素 (可自定义比较器)
adjacent_find在区间中寻找第一次出现连续两个相等元素的位置 (可自定义比较器 )
search在区间中查找另一个区间第一次出现的位置 (可自定义比较器 )
search_n在区间中查找第一次出现等于某值的连续 n个元素(可自定义比较器 )
equal判断两区间是否相等 (可自定义比较器 )
mismatch逐个比较两区间的元素,返回第一次发生不相等的两个元素的位置 (可自定义比较器 )
lexicographical_compare按字典序比较两个区间的大小 (可自定义比较器 )

举几个例子:

count_if
template<typename InIt, typename Pred>
size_t count_if(InIt first,InIt last,Pred pr);
计算[first,last)中符合pr(e)==true的元素e的个数
//查找v中元素值等于4的元素
vector<int> v{1,2,3,4,5};
count_if(v.begin(),v.end(),[](int n)->bool{return n==4;})

顺便复习lamda表达式,lamda表达式是一个可调用对象,可将其理解为一个未命名的内联函数,可以定义在函数内部,具有如下形式:
[捕获列表] (参数列表) -> 返回类型 {函数体}
捕获列表指lamda使用其所在函数的参数列表(包括值捕获和引用捕获),lamda参数列表中不能有默认参数。

min_element
max_element
class A {
public: int n;
A(int i):n(i){}
};
bool operator<(const A & a1, const A & a2) {
if(a1.n==3 && a2.n==7)
	return true;
return false;
};
int main() {
	A a[5] = {3,5,7,2,1};
	cout<<min_element(a,a+5)<<endl;
	cout<<max_element(a,a+5)<<endl;
}

min_element默认是采用 "<"进行比较的,首先它会先把最小值min初始化*a,然后判断区间中的下一个元素 *next < min?,如果成立,就让min=*next,再判断下一元素,对于重载的 “<”,也就是执行operator<(*next,min);最后输出3。
max_element默认也是采用 "<"进行比较的,它执行的则是operator(max,*next),最后输出的是7。

2.变值算法

此类算法会修改源区间或目标区间的值,值被修改的那个区间,不可以是属于关联容器的(set,multiset,map,multimap,以及unordered版本)

名称效果
for_each对区间中每个元素做某种操作
copy复制一个区间到别处
copy_backward复制一个区间到别处,目标区间是从后往前被修改
transform将一个区间元素变形后拷贝到另一区间
swap_ranges交换两个区间的内容
fill用某个值填充区间
fill_n用某个值替换区间中的n个元素
generate用某个操作的结果填充区间
generate_n用某个操作的结果替换区间中的n个元素
replace将区间中的某个值替换为另一个值
replace_if将区间中符合某种条件的值替换成另一个值
replace_copy将一个区间拷贝到另一个区间,拷贝时某个值要换成新值拷贝过去
replace_copy_if将一个区间拷贝到另一个区间,拷贝时符合条件的某个值要换成新值拷贝过去

举例:

transform
template<typename InIt,typename OutIt,typename Unop>
OutIt transform(InIt first,InIt last,OutIt x,Unop uop);

对[first,last)中的每个迭代器I,执行uop(*I),并将结果放入x开始的地方,要求uop(*I)不改变 *I的值。

3.删除算法

删除算法会一个容器里的某些元素。这所说的“删除”,并不会使容器里的元素减少,其工作过程是:将所有应该被删除的元素看做空位子,然后用留下的元素从后往前移 ,依次去填空位子。元素往前移后,它原来的位置也就算是空子,也应由后面留下的元素来填上。最后,没有被填上的空位子,维持其原来的值不变。 删除算法不应该作用于关联容器。

名称效果
remove删除区间中等于某个元素的值
remove_if删除区间中满足某个条件的值
remove_copy拷贝区间到另一个区间,等于某个值的元素不拷贝
remove_copy_if拷贝区间到另一区间,符合某种条件的元素不拷贝
unique删除区间中连续相等的元素,只留下一个(可自定义比较器)
unique_copy拷贝区间到另一个区间,连续相等的元素,只拷贝第一个到目标区间(可自定义比较器)

举例:

unique
template<typename FwdIt,typename Pred>
FwdIt unique(FwdIt first,FwdIt last,Pred pr)

用pr比较是否相等。对于[first,last)区间中连续相等的元素,只留第一个。返回迭代器,指向元素删除后的区间的最后一个元素的后面。

4.变序算法

变序算法改变容器中元素的位置,但是不改变元素的值,变序算法不适用于关联容器。此类算法复杂度都是O(n)的。

名称效果
reverse颠倒区间的前后次序
reverse_copy把一个区间颠倒后的结果拷贝到另一个区间,源区间不变
rotate将区间进行循环左移
rotate_copy将区间以首尾相接的形式进行旋转后的结果拷贝到另一个区间,源区间不变
next_permutation将区间改为下一个排列(可自定义比较器)
pre_permutation将区间改为上一个排列(可自定义比较器)
random_shuffle随机打乱区间内元素的顺序,使用前要初始化伪随机种子
partition把区间内满足某个条件的元素移到前面,不满足该条件的移到后面
stable_partition把区间内满足某个条件的元素移到前面,不满足该条件的移到后面。并且对于这两部分元素,分别保持它们原来的先后次序不变

举例:

template<typename InIt>
bool next_premutation(InIt first,InIt last)

string str = "213";
while(next_premutation(str.begin(),str.end()))
{
	cout<<str<<endl;
}

分析:next_permutation默认用operator < 比较元素,会改变[first,last)区间元素的次序,使它们符合“下一次排列次序”。如果元素排成正规次序(也就是字典顺序),则算法返回false。因此输出为:231\n312\n321\n

5.排序算法

排序算法比变序算法复杂度高,一般是O(nlogn)的,排序算法需要随机访问迭代器的支持,因此不适用于关联容器和list。

名称效果
sort将区间从小到大排序(可自定义比较器)
stable_sort区间从小到大排序,并保持相等元素间的相对次序(可自定义比较器)
partial_sort对区间部分排序,直到最小的n个元素就位(可自定义比较器)
partial_sort_copy将区间前n个元素的排序结果拷贝到别处,源区间不变(可自定义比较器)
nth_element对区间部分排序,使得第n小的元素(n从0开始算)就位,而且比它小的都在它前面,比它大的都在它后面(可自定义比较器)
make_heap使区间称为一个“堆”(可自定义比较器)
push_heap将元素加入一个是“堆”的区间(可自定义比较器)
pop_heap从“堆”区间中删除堆顶元素(可自定义比较器)
sort_heap将一个“堆”区间排序,排序结束后,该区间就是普通的有序区间,不再是“堆”了(可自定义比较器)

sort实际上是快速排序,时间复杂度O(nlogn);平均性能最优,但是最坏情况下,性能非常差。
stable_sort实际上是归并排序,特点是能保持相等元素之间的先后次序;有足够内存的情况下,复杂度为nlogn,否则复杂度为nlognlogn。
排序算法要求催寄存区迭代器支持,因此不适用于关联容器和list。
堆排序:堆是一种二叉树,最大元素总是在堆顶上,二叉树任何节点的子节点总是小于等于父节点的值。即k(i)>=k(2i+1)且k(i)>=k(2i+2),其中i=0,1,2…。对的各种排序算法,需要随机访问迭代器支持。
举例:

//按升序排序。判断x是否应该比y靠前,就看x < y是否为true
template <typename RanIt>
void sort(RanIt first,RanIt last);
//按升序排序。判断x是否应该比y靠前,就看pr(x,y)是否为true
template <typename RanIt,typename Pred>
void sort(RanIt first,RanIt last,Pred pr);

class MyLess{
public:
	//n2个位数大于n1个位数,就返回true
	bool operator()(int n1,int n2){   
		return n1%10 < n2%10;
	}
};
int main(){
	int a[] = {14,2,9,111,78};
	sort(a,a+5,MyLess());
	for(auto &ai:a){
		cout<<ai<<" ";
	}
	cout<<endl;
	sort(a,a+5,greater<int>());
	for(auto &ai:a){
		cout<<ai<<" ";
	}
}

分析:MyLess类重载了operator()运算符,因此MyLess()是一个函数对象,可以直接作为sort的第三个参数,greater< int> () 同理;第一个sort判断pr(x,y)是否为true的逻辑是x的个位数小于y的个位数,就返回true,如果pr(x,y)是true,x就应该排在y的前面(因为是升序排列),也就是说谁的个位数小,谁就排在前面。第二个sort判断pr(x,y)是否是true的逻辑是x大于y,就返回true,也就是说大的应该排在前面。因此最终输出为:111 2 14 78 9 \n 111 78 14 9 2\n.

6.有序区间算法

有序区间算法要求所操作的区间是已经从大到小排好序的,而且需要随机访问迭代器的支持。所以有序区间算法不能用于关联容器和list。

名称效果
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将两个连续的有序区间原地合并为一个有序区间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值