Maximum Subarray
题目链接:https://oj.leetcode.com/problems/maximum-subarray/
runtimes:12ms
一、问题
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.
二、分析
找出序列[−2,1,−3,4,−1,2,1,−5,4]中和最大的子序列,按照传统的思维,需要枚举每一个以i元素为首的子序列,求得所有和之后比较选择最大的和的那个序列,这样需要O(n^2)的时间,显然不符合题目的要求。那么一定要在O(n)内找出答案,就只能遍历一次。想了很久没有想明白其中的所以然,于是乎找人讨论,给了一个小于零的和无效的想法,即前面N-1个数的和如果不超过零,那么对Nth数的和没意义,就舍去前N-1个数的和,从Nth开始计算和。想想觉得挺有道理,但是要证明还是有一定难度。(证明:既然前N-1个数的和加上去使得比A[i]还要小,那么就没必要加上,直接从A[i]开始累积反而起点更高。假如前N-1个数的和为S1,且S1 + A[N] < A[N],即S1 < 0,此时有包含Nth数的最大和子序列,其和为S2,假设存在包含Nth数的另外一个和最大子序列,同时包含了N-1个数,那么有S1 + A[n] + S2 - A[n] > A[n] + S2 - A[n],即S1 > 0,与原题设不符,因此不存在酱紫的序列。)
三、总结
1、枚举每一个以i元素为首的子序列,其中和最大的子序列即为所求答案。
2、从头扫描数组,假如扫描到Nth个数,如果N-1个数的和不超过零,那么舍去,重新从Nth个数开始累加;如果大于零,则继续累加。过程中需要记录每次累加的到的和,并保留最大的那个和。
3、从运行时间分布来看,速度分别是c++ > python > java,C++太高效了,赞!
四、方案
方案一:
class Solution {
public:
int maxSubArray(int A[], int n) {
int maxCount = INT_MIN, temp;
for (int i = 0; i < n; i++)
{
temp = A[i];
for (int j = i + 1; j < n; j++)
{
temp += A[j];
if (temp > A[i])
{
A[i] = temp;
}
}
if (A[i] > maxCount)
maxCount = A[i];
}
return maxCount;
}
};
方案二:
class Solution {
public:
int maxSubArray(int A[], int n) {
int sum = 0, record = INT_MIN,counter = 0;
for (int i = 0; i < n; i++)
{
if (A[i] <= 0)
{
counter++;
}
sum += A[i];
if (sum <= 0)
{
sum = 0;
}
else{
record = record < sum ? sum : record;
}
}
if (counter == n)
{
record = A[0];
for (int i = 1; i < n; i++)
{
record = record < A[i] ? A[i] : record;
}
}
return record;
}
};
五、反思
找到了一篇详细分析的文章,使用了动态规划的思想,扫描数组,每个元素算一个阶段,在阶段中的状态是A[i],决策是选择sum + A[i]和A[i]中最大者作为新阶段的出发点,与前N-1项和无效一样,只不过我是以0为分界点,小于零就舍去,该程序是取较大者,酱紫就省去了排除负数的阶段,赞!状态转移方程是sum = Max(sum + A[i], A[i]),同时要保留在这个过程中的最大和值。用了java,程序的运行时间是235!
原文链接:http://www.programcreek.com/2013/02/leetcode-maximum-subarray-java/
另外重新对动态规划思想进行认识,这篇文章的摘要写的挺好:http://www.cnblogs.com/lvpengms/archive/2010/02/03/1663055.html