算法概述及基本算法

算法概述及基本算法

一.算法概述

算法,即解决逻辑或数学问题的步骤,STL中的算法是把常用的可复用的。某些算法要搭配固定的数据结构(如heap的算法,树的算法)。

1.算法分析与复杂度表示

算法分析即对算法的时间复杂度和空间复杂度分析,一般使用大O表示法,至于对算法复杂度的计算,这个又是另外一个学习的内容了,这里不作描述。

2.STL算法总览

img

img

img

img

3.质变算法mutating algorithms——会改变操作对象之值

算法操作的区间要么是一个单值,要么是迭代器区间[first,last)所指向的区间,质变算法就是会改变这个区间的值的算法,不过要注意,对于常数来说尝试改变会报错。

4.非质变算法nonmutating algorithms–不改变操作对象之值

这个和上面的相反,算法本身不会改变迭代器所指区间的值。

5.STL算法的一般形式

所有泛型算法的前两个参数都是一对迭代器,即[first, last),算法本身对迭代器的类别也会有一定的要求,没有达到一定的要求可能在编译阶段不会报错。

许多STL算法不止支持一个版本,例如有的自带缺省函数,有的也可以传入仿函数(有时以xxx和xxx_if区别),有的就地进行(在该容器进行),有点另地进行(在另一个容器副本进行)(有时以xxx和xxx_copy区别)。

二.算法的泛化过程

所谓算法的泛化,就是指让算法脱离特定的数据结构,使得泛化后的算法可以为多个数据结构所用(只要符合算法的接口),而STL的泛化很明显主要是用模板类和迭代器实现的。

三.数值算法

要使用数值算法,头文件需包含

1.accumulate(元素累计)

T accumulate(InputIterator frist,InputIterIator last,T init,[BinaryOperation binary_op])

基本计算单元为init = init op *first

在未提供二元操作函数的情况下,accumulate函数的作用是init+迭代器区间内的每一个数,

在提供二元操作函数的情况下,accumulate函数的作用是init = binary_op(init, *i)(原来这里就是一个加法)

2.adjacent_difference(相邻元素的差额)

基本计算单元为*++result = *first op *(first-1) 注意:这里的两个值都是指原序列的值

其实数值函数都差不多,都是有自有的函数,也可以重新传入函数,这个如果没有传入二元操作函数就是相邻元素的差额,如果传入了二元操作函数就是按传入的操作计算,注意value_type()提取的是真正类型的指针。

3.inner_product(内积)

基本计算单元为init = init op1 (*first1 op2 *first2)

一共有两个序列,然后对这两个序列的每个对应元素进行操作然后再对操作结果进行操作,如果要求一个序列内的结果,可以让first2=first1

4.partial_sum(局部求和)

基本计算单元为*++result = *result op *first 注意:partial_sum和adjacent_difference互为逆操作,即两个函数都执行后序列保持原状,但是两者的基本计算单元有本质的不同,partial是用的已改变的前一个,而adjacent用的是未改变的前一个。

5.power(幂次方)

这个没什么好说的,就是x执行n次op,只不过STL实现得很有技巧啊,因为最简单的实现方式就是执行n次,如果分成两半采用递归时间复杂度可以降为O(logn),但是递归始终有点不方便,还占栈,所以书上的代码原理是这样的:

如果n为偶数,则二进制最后一位必为0,那么就让n中的2直接进去,即Xn变为X2^n/2,一直这样直到n变为奇数,当n变为奇数后取出一X则n又变为偶数,最后乘以所有取出来的数即可。(注意:奇数右移1即隐含了减1的意思)

参考:

STL 中的 power 函数实现

STL源码剖析之算法:power

6.iota(在某区间填入某指定值的递增序列)

基本运算单元为*first++ = value++

四.基本算法

STL中并没有区分什么基本算法或复杂算法,但有一些算法定义在<stl_algobase.h>中,而其他算法定义在<stl_algo.h>中,因此我们把前者的算法叫做基本算法。

1.部分基本算法

equal(判断两个区间相等与否)

equal的实现就是依次比较两个区间的元素,注意,equal基于一个假设:即第二个区间的长度不小于第一个区间,如果小于了,经过测试,返回false,如果第一个区间小于第二个区间但是前面的相同的话还是返回true,所以要判断两个区间是否完全相同还要自行额外判断区间长度是否相同。

equal也可以接受一个仿函数(函数指针)作为参数。

fill(改填元素值)

这个没什么好说的,就是给区间内每个元素赋一个传进来的值。

fill_n(改填元素值,n次)

将[first,last)的前n个元素改填新值,注意n不能超过区间大小。

iter_swap(元素互换)

互换两个迭代器所指元素。

lexicographical_compare(以字典顺序进行比较)

以字典顺序比较两个区间,若第一个区间小于第二个区间则返回true(包括第一个区间等于第二个区间前面部分但长度更小),其他情况返回false。也可以自行传递函数,还有针对const unsigned char*和const char *的特化版本。

max(最大值)

取两个对象中的大值,可自行传递函数。

min(最小值)

取两个对象中的小值,可自行传递函数。

mismatch(找出不匹配点)

比较两个序列,返回一个pair,first是第一个序列不匹配点,second是第二个序列不匹配点,第一个序列长度默认应该不大于第二个序列(经过测试,好像大于了也没什么事)。可以自行传递函数。

swap(交换)

交换两个对象的内容。

2.copy(复制)

copy算法是客端程序和STL内部都常用的算法,因为copy的经常使用,所以STL用了很多方法来提高copy算法的效率,包括函数重载,型别特性(type traits),偏特化等。

è¯·æ·»åŠ å›¾ç‰‡æè¿°

copy算法可将输入区间[first, last)内的元素复制到[result, result + (last - first))内。

如果输入区间与输出区间没有重叠,copy算法是没有问题,但是如果有重叠,特别是输出区间开头在输入区间内copy算法可能有问题,之所以是可能有问题,是因为copy算法有可能根据迭代器特性调用memmove,这个函数是直接复制原区间内容再粘贴到输出区间。

copy算法的结构:copy算法只有一个接口,这个接口对应的是一个最泛化的版本和两个特化版本(那两种char),如果是那两种特化版本,函数是直接调用memmove(),这个最泛化版本调用_ copy_dispatch()接口,_ copy_ dispatch接口也对应一个泛化版本和两个特化版本(T*和const T *),如果是泛化版本,调用_ _ copy()接口,同时提取迭代器特性,这个接口对应InputIterator版本和RandomAccessIterator版本,前一个以迭代器等同于否来进行复制行为,后一个调用copy_d接口,这个接口对应的函数以n决定循环的次数,速度更快;如果是特化版本,判断指针所指之物是否有trivial assignment operator(平凡赋值操作符),如果为true那么用memmove(),如果为false则用_copy_d()(注意这里的true和false不是bool型)。

关于trivial assignment operator:所谓~~~,从trivial的意思也可以看出来,是琐碎的,无意义的赋值,即可以直接通过内存赋值,不需要调用构造函数什么的,例如int,char什么的,而non-trivial assignment operator是指有意义的赋值,要用=符号一个个赋值,trivial assignment operator都是规定好了的,在<type_traits.h>中,我们直接创建的class都死non-trivial assignment operator,如果要使其为trivial assignment operator,那么就要改上面那个文件了。

3.copy_backward(逆向复制)

逆向复制的逆向体现在两点,一是从last1-1开始复制,二是先复制到result,再复制到result-1,具体的实现和copy差不多,可能就实际的代码方向改了点,当result在[first,last)里面有可能有问题。

五.set相关算法

set一共提供了四种与set(集合)相关的算法,分别是并集(union),交集(intersection),差集(difference),对称差集(symmetric difference)。

SLT中的set和multiset(允许重复元素的set)都是有排序的,所以这四个算法接收的是有序区间,hash_set和hash_multiset无序,所以不能用这四个算法。

四个算法至少有四个参数,分别表现两个set区间,第一版本以a<b判断相等,第二版本以自行传递函数指定a<b的意义。(这里只提供第一版本)

1.set_union(并集)

算法取S1和S2的并集,S1,S2为为有序序列,S1和S2中的元素不需唯一,假设某个值在S1中出现n次,在S2中出现m次,根据代码逻辑,最终并集中元素个数为max(m, n),其中n个来着S1,剩下的来自S2。

具体操作的逻辑见书,还是比较简单。

2.set_intersection(交集)

算法取S1和S2的交集,假设某个值在S1中出现n次,在S2中出现m次,根据代码逻辑,最终交集中元素个数为min(m, n),全部来自S1。

具体的操作逻辑见书,要记住S1和S2都是有序序列,有时候有序是一个很神奇,很有效率的东西。

3.set_difference(差集)

算法取S1和S2的差集,即S1-S2,假设某个值在S1中出现n次,在S2中出现m次,根据代码逻辑,最终差集中元素个数为max((n-m),0),

具体的操作逻辑见书。

4.set_symmetric_difference(对称差集)

算法取S1和S2的对称差集,对称差集,是对应数学中集合的对称差。集合A与集合B的对称差集定义为集合A与集合B中所有不属于A∩B的元素的集合,记为A△B,也就是说A△B={x|x∈A∪B,x∉A∩B},即A△B=(A∪B) - (A∩B)。

假设某个值在S1中出现n次,在S2中出现m次,根据代码逻辑,最终对称差集中元素个数为|n - m|。

具体的操作逻辑见书。

六.heap算法

共有四个heap算法,make_heap(制造一个heap),pop_heap(从heap中取出一个元素),push_heap(将一个元素推进heap内),sort_heap(给heap排序),具体实现见以前heap的内容。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值