算法导论:最大子序列和

算法导论:最大子序列和

问题描述:
什么是最大子序列和呢?
就是给定一组序列,所有子序列中和最大的那一组序列。
比如这里给出一组序列{-2,11,-4,13}


子序列

这里列出了10个子序列。


最大子序列和

其中20就是我们要求的最大子序列和。
关于最大子序列和有几个注意事项:
1. 空序列也是子序列,它的和为0;
2. 如果序列中所有整数均为负数,则最大子序列和为0;
3.子序列一定是连续的;

穷举法1

算法思想:穷举所有子序列,找最大的。

穷举法1

如上图所示,列出所有子序列,再找出其中最大的和,不过这其中会出现大量重复的计算,比如-2+11就计算了三遍,耗费大量时间,下面计算时间复杂度。
n个数序列

假如是有n个数的序列,那它会有多少个子序列呢?从1开始的子序列有n个,从2开始的子序列有n-1个,所以总共的子序列为:
总序列数

现在我们知道了总序列数,也就是序列的个数,那这些序列和的计算次数是多少?
计算次数

由1开始的子序列有n个,从{1,2}一直到{1,2····n-1,n},我们在计算这些序列和时,{1,2}相当于计算了1次,就是1+2。计算{1,2,3}时我们又计算了一次1+2,所以这次计算了2次,那由1开始的序列计算的次数就是1+2+····+n也就是图中所示的结果。2开始的序列的计算次数就是1+2···+n-1,以此类推求出所有序列的计算次数,最后得出总的计算次数。所以时间复杂度为O(n3)

穷举法2

算法思想:在某点开始的所有序列中找最大的,找最大的。

比如这里给出一组序列{-2,11,-4,13},首先计算由-2开始的所有子序列和,再找出其中最大的

穷举法2

可以看到这种方法避免了穷举法1中每次都要从头开始相加而造成的大量浪费,下面我们依次计算由11,-4,13开始的序列,找出最大子序列和。
穷举法2

在分别求出以-2,11,-4,13开始的最大子序列和后,再选择其中最大的那个,就是我们要找的最大子序列和。但是这其中依然包含了重复计算,如上图所示,计算由-2开始的序列时,我们计算了11+-4+13,在计算由11开始的序列时,也依然计算了11+-4+13,造成了重复计算。
那所有这些序列的总共计算次数是多少呢?
时间复杂度

在穷举法2中,由1开始的序列有n个,但因为避免了穷举法1中的重复计算,所以计算次数只有n次,总共的计算次数就是1+2+···+n次,所以时间复杂度是O(n2)。

分治法

分治法的设计思想:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

给出一个序列{a,b,c,d,e,f,g},显然最大子序列和只可能出现在三个地方:

1、输入序列的左半部分;
2、输入序列的右半部分;
3、跨越输入序列的中部而位于左右两个部分;

所以我们只需要分别找出这三个部分的最大子序列和,再比较这三个和的大小,找出最大的子序列和即可。

分治法

我们可以通过递归的方法找到左半部分最大子序列和lsum以及右半部分最大子序列和rsum。
分治法

找到跨越中部的最大子序列和,因为这个子序列和一定是跨越中部的,所以只需要确定好中部,然后使lmax以及rmax都为最大,那么csum就是我们要找的子序列。最后比较这三个子序列的大小,最大的和就是最大子序列和。这里使用的是分治法,分治法的时间复杂度为O(nlgn),详解请看分治法

动态规划法

分析:
1、因为最大子序列和的最小取值为0,所以最大子序列的第一个元素不会是负的。
若a[i]<0,则a[i]开头的序列,一定不如a[i+1]开头的序列大。
2、同理任何负的子序列,不可能作为最大子序列和的前缀。


动态规划法

这里的{4、-5}不能作为子序列的前缀。
3、如若一个子序列和thisSum>0(其中thisSum是从第m项到第n项的和),那么最大子序列和一定不会以a[m+1]和a[n]之间的项开始。


动态规划

这里{1、5、-3、2}这个序列和大于0,那么最大子序列和就不会是从5或者-3或者2开始的,因为由这些开始,加上前面的1会更大。
下面将用介绍具体的例子:
首先给出序列{-5、15、-2、13、-9、-1、9、-30、10、3}。
动态规划

用标记分别标出,子序列和的起始位置,最大子序列和起使以及终点位置,此时三个位置都在-5这里。并且记录下该时刻最大子序列和的值以及子序列和的值。
动态规划

当指针移动到15时,根据上面的第一条规则,最大子序列的第一个元素不为负数,所以此时三个标记都要移动到15,子序列的和为15,同时更新最大子序列的和15。


动态规划

当指针移动到-2时,更新此时的子序列和为13,发现13<15,所以最大子序列和依然是15,且起使、终点位置也不改变。
动态规划

当指针移动到13时,子序列和变为26>15,所以更新最大子序列和为26。并且最大子序列的终点位置也要发生变化。
动态规划

在指针移动过-9、-1、9、-30时子序列和的值都没有26打,所以最大子序列和并未更新,不过指针移动到-30时子序列和变为了-5,也就是{15、-2、13、-9、-1、9、-30}和为-5,根据规则2,这个序列一定不会是最大子序列的前缀。而且{15、-2、13、-9、-1、9}这个序列的和是大于0的,根据规则3,最大子序列和一定不会是从{-2、13、-9、-1、9}这里面的某一项开始的。所以最大子序列和不会是以{15、-2、13、-9、-1、9、-30}为前缀开始的,也不会是以{15、-2、13、-9、-1、9、-30}中的某一项开始的。那么当我们下一步,移动子序列起使位置的标记时,就可以跳过这里面所有的数,移动到-30后面的数。
动态规划

子序列和的起使位置变为了10,10<26,所以最大子序列和的值以及标记依然不发生变化。
动态规划

序列遍历完成后,可从记录中得知,最大子序列和为26,序列是{15、-2、13}。
在整个过程中,我们只用指针遍历了一遍序列,并且做了简单的记录,所以动态规划算法的时间复杂度是O(n)。

总结

穷举法1:
算法思想:穷举所有子序列,找最大的。
时间复杂度:O(n3)
缺点:包含大量重复计算。
穷举法2:
算法思想:在某点开始的所有子序列中,找最大的。
时间复杂度:O(n2)
缺点:同样包含重复计算。
分治法:
算法思想:利用可分解、递归的性质,采用分治策略。
时间复杂度:O(nlgn)
动态规划法:
算法思想:采用了动态规划的方法。
时间复杂度:O(n)

你可能感兴趣的:(算法导论:最大子序列和)