1、题目
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
这是Leetcode上tag为easy的题,也是一道很经典的题,这里提供两种方法去解决它。
动态规划法:
我们知道:当加上一个正数的时候,和会增加,当我们加上一个负数时,和会减少。对于最大子序和问题,如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。
class Maximum_Subarray_Solution {
/***
* 扫描法:(动态规划)
* 当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。
* 如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,
* 不然的话这个负数将会减少接下来的和。
* @param nums
* @return 最大子序和
*/
public int maxSubArray(int[] nums) {
int sum = nums[0];
int num = nums[0];
int len = nums.length;
for (int i = 1; i < len; i++) {
if (sum > 0) {
sum += nums[i];
}else {
sum = nums[i];
}
num = Math.max(num,sum);
}
return num;
}
}
进阶
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
这里用分治法实现的代码,时间复杂度为O(nlogn)
class Maximum_Subarray2_Solution {
/**
* 采用分治法去求最大子序串
* 数组从中间分开,最大子序串要么在左边,要么在右边,或者左右都有。
* 在左和在右用递归,在子序串左右都有的情况,分别求最大,在求和。
* @param nums
* @return 最大子序串
*/
public int maxSubArray(int[] nums,int from,int to) {
if (from == to) return nums[from]; //分治递归要注意出口条件
int middle = (from + to) / 2;
int m1 = maxSubArray(nums,from,middle);//要有递归信任,不要纠结层层深入
int m2 = maxSubArray(nums,middle + 1,to);
//针对子序串在两边的情况
int left = nums[middle];
int now = nums[middle];
for (int i = middle - 1; i >= from; --i) {
now += nums[i];
left = Math.max(now,left);
}
int right = nums[middle+1];
now = nums[middle+1];
for (int i = middle + 2; i <= to; ++i) {
now += nums[i];
right = Math.max(now,right);
}
int m3 = left + right;
return max(m1,m2,m3);
}
public int max(int a, int b, int c) {
int ret = 0;
if (a > b) {
ret = a;
} else if (a <= b) {
ret = b;
}
if (ret >= c)
return ret;
else
return c;
}
}