【Divide and Conquer】53.Maximum Subarray(easy)
#week2#
#from leetcode#
Description
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4]
,
the contiguous subarray [4,-1,2,1]
has the largest sum = 6
.
Analysis
题目隶属于分治类型之下,所以最开始就会从 “是否能够拆分成一个个子问题” 这个角度出发去思考,最开始想到,能否将其从n个数的最大和数列变为n-1个数的最大和数列,然后慢慢地递减下去,于是最开始第一种思考,从后面一个个减去一个数组元素,也就是思考max(n)=max(n-1)+0?a[n]是否可以从某个分治规则成立(其中a为数组,max(n)代表前面n个数构成的数组的最大和连续数列),然而思考后,发现这样是无法成立的,因为递归求解前n个数的数组的最大和连续数列得到最大和之后,我们需要考虑如何能够与后面这个数联系起来呢,首先,我们就要保证最大连续数列的边缘是接触到后面的这个数的,这样才能够把这个数算入其中,这样假设我们得到了max(n-1),那么我们递归返回来的信息有限,我们如何知道第n-1个数是包含在里面的呢?其实也非行不通,但是比较麻烦,所以暂且搁置第一个想法。
第二个想法,我就从1,2,3,4,...,n-1,n个连续数列的最大连续数列和出发去思考,1个就是本身,2个的话,就取决于大的或者两者的和,然后三个、四个,出现问题了,也是比较难以找到其关系,这个关系规则比较繁琐,而且这种划分很多,相邻2个、相邻3个,划分个数很多,所以比较麻烦,于是这个想法也搁浅。
第三个想法,我又回到了第一个想法的类似方向,不过这一次不想从递归走了,想要反面来思考一下,换成递推会如何呢?一般来说,递推相对于递归会更快,有的时候思路会比较清晰一点,不过其实大部分递归比递推好想。好,那我们从最前面的1个开始,然后前面的2个、3个、....、直到n个,依旧用max(n)来记录n个元素的数组中最大连续数列和,而a是所给的数组,同时我们记录下max(n)里面的连续最大序列的右边界right(数组的下标),那么max(1)就是a[1](无论a[1]是否大于零,因为连续数列至少有一个数,所以就算a[1]小于0,如果数组只有一个值,那么max也是a[1]而不是0),然后max(2)要怎么考虑呢,其实这个就可以转换为一般思想的考虑了,如果确定了max(n-1),要入一个a[n],应该如何考虑max(n)?我们一起来思考一下:
- 如果a[n]>=max(n-1)的话,我们考虑max(n-1)<=0,那么很明显,max(n-1)可以丢弃,我们从right边缘向a[n]考虑;
- 如果a[n]<max(n-1),那么我们思考几种情况:
- a[n]<0,那么a[n]可以丢弃不算入max(n)中;
- a[n]=0,那么如果max(n-1)的右边界right=n-1,则可以将a[n]加入max(n)中,因为加入他之后总和未改变,同时可以与后续的数成为连续最大数列;
- a[n]>0,那么我们就也可以从right边缘向a[n]]考虑同时与max做比较;
- 分情况大致完成,解释一下从right边缘向a[n]考虑的意思:即从right边缘+1的数开始加到a[n],得到∑i=right+1->n,然后再考虑∑i=right+2->n,直到∑i=n(不过求这些只要从n开始向前加即可得到每一个的值),得到∑max,第一步的那个就是将∑max作为max,而最后一步中提到与max做比较,即将∑max和max(n-1)+∑i=right+1->n作比较,即max(n)=max(∑max,max(n-1)+∑i=right+1->n)。
这个思路是花了点时间思考总结出来的,所以大家可以自己思考思考,如果有问题请指出。
虽然说这个思路比递归来说清晰了许多,但是可以发现,还是比较麻烦的,最坏的情况复杂度可以达到O(n),即跑n次,且每一次都需要与前面的数进行相加,因此十分耗时。
但是,到了这个时候我已经才思枯竭了,不太想得出如何做优化,或者可以用什么其他的方法来做,于是跑去看了下其他人的discussion。
发现第一篇discussion和我的思考很类似,但是最后他通过很简单的一个式子
maxSubArray(A, i) = maxSubArray(A, i - 1) > 0 ? maxSubArray(A, i - 1) : 0 + A[i];
即得到了n个数的数列与n-1个数的数列的关系,最开始我觉得似乎没有涵盖所有情况,但是后面推导发现所有情况都被这个式子给解决了,很神奇,但是给我自己思考,我确实没有找到思考出这个思想的切入点,实际上它这个答案也与后面的一个答案类似,他们就是考虑到当出现了sum<0的情况就将这个sum抛弃,重新开始记录sum,很巧妙,而且很快时间复杂度仅仅O(n)。
我也接纳了这个方法,因为自己的方法实在繁琐,我能够理解该方法解题的正确性,但是不太理解是如何去想到这个方法的,如果聪明的大家知道的话请指引,谢谢~
下面给出这两个discussion的链接和名称:
https://leetcode.com/problems/maximum-subarray/discuss/
第一篇DP solution & some thoughts
第三篇Simplest and fastest O(n) C++ solution
Code
1 class Solution { 2 public: 3 int maxSubArray(vector<int>& nums) { 4 int max = nums[0], sum = 0; 5 for (int i = 0; i < nums.size(); i++) { 6 sum += nums[i]; 7 if (sum > max) max = sum; 8 if (sum < 0) sum = 0; 9 } 10 return max; 11 } 12 };