Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
解析
题目要求,让我们找到连续的子数组,注意是连续的和最大的数组。最后返还和。这道题可以用动态规划和贪心算法结合的思想来解答,从而达到线性的时间复杂度和O(1)的空间复杂度。
使用动态规划,首先我们需要找到: 1. 初始状态。2. 状态转移方程。
初始状态很好理解,就是我们需要输出结果的最开始的状态,那什么是状态转移呢?个人认为这是新同学最容易困惑的地方。所谓状态转移,就是我们要求现在的值。那么这个值,是由之前的某个计算值,转移过来的。如果不理解的话,我们就来开这道题具体的例子就能很快的理解了。我们首先用一个动态规划的数组来解答。然后我们把它优化到O(1)。
我们要求最大的和,那么我们知道我们至少要把整个数组遍历一次我们才能知道最大的和在哪个位置。所以我们的初始状态就是数组的第一个元素 -2。我们来看这个dp数组。
dp数组 第一行是index
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
我们可以观察到,在我们的index是1的时候,我们有两个选择,我们可以把现在这个位置的值,num[1] = 1的值,和之前前一位的值相加,但是我们也有另外一个选择,就是不加前一位的值,1独立构成一个新的子数组。他的和就是他的本身。那么我们发现,不管加不加,我们1这一位的值,是从前一位nums[0]的值,转移过来的。那么我们的状态转移方程就一目了然。我们用这个方法来填满我们的dp数组。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-2 | 1 | -2 | 4 | 3 | 5 | 6 | 1 | 5 |
在这里我们其实也用到了一些贪心算法的概念,因为我们要保证前一位的数字是正数。下面是一个未优化的代码实现。
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 0) return -1;
int res = nums[0];
int[] dp = new int[nums.length];
dp[0] = nums[0];
for(int i = 1; i < nums.length; i++) {
dp[i] = nums[i] + (dp[i-1] > 0 ? dp[i-1] : 0);
res = Math.max(res, dp[i]);
}
return res;
}
}
我们会发现,其实我们并不需要重新定义一个新的dp数组。因为,我们发现我们的结果总是基于前一位,那么我们可以用一个变量来记录他,并且在每遍历一个新元素的时候,我们更新他就好。下面是一个优化的代码。
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int maxSum = nums[0];
for(int i = 1; i < n; i++) {
if (nums[i - 1] > 0) {
nums[i] += nums[i - 1];
}
maxSum = Math.max(nums[i], maxSum);
}
return maxSum;
}
}
Time Complexity:O(N) 因为我们遍历了所有的元素
Space Complexity:O(1) 没有额外的空间栈的使用