问题描述:
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶: 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
共用了三种方法:
第一种: 暴力求解法 时间复杂度 < O(n^2)
class Solution {
public int maxSubArray(int[] nums) {
// 先用暴力法求解
int max = nums[0]; // 默认第一个最大
for(int i = 0; i < nums.length; i++){
int sum = 0;
for(int j = i; j < nums.length; j++){
sum += nums[j];
max = max>=sum?max:sum;
}
}
return max;
}
}
大约耗费资源情况:
109 ms | 41.5 MB |
思路:
开始默认最大值maxVal 是数组第一个数,然后从数组 下标为 0 开始,求 0 到 nums.length -1 序列的和,与当前最大值比较,处理是否需要更新最大值。 接下来 再从 下标 1 开始, 求 1 到 nums.length -1 序列的和, 与当前最大值比较,处理是否需要更新最大值。然后,做同样的处理,直至 下标 到 nums.length - 1 的和。 此时获得的最大值就是要求的最大连续子序列和。
第二种:动态规划思想
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 0){
return 0;
}
if(nums.length == 1){
return nums[0];
}
// 非循环
int maxVal = nums[0];
int subSumMax = 0;
for(int x = 0; x < nums.length; x++){
subSumMax += nums[x];
subSumMax = subSumMax > nums[x]?subSumMax:nums[x];
maxVal = maxVal>subSumMax?maxVal:subSumMax;
// System.out.println(maxVal);
}
return maxVal;
}
}
使用资源情况:
2 ms | 38.7 MB |
思路:(最优方法)
subSumMax表示当前累加和最大值, maxVal表示 全局最大值。
首先 subSumMax 加上当前nums[x] , 然后加和结果 subSumMax与nums[x]进行比较取最大值者 给subSumMax:
subSumMax += nums[x];
subSumMax = subSumMax > nums[x]?subSumMax:nums[x];
其实上面就是判断, 目前的累加和是否比当前下标索引所在的处的值nums[x]相比是否更大,不然用当前值nums[x]更新他。
最后再拿当前累加和最大值与全局最大值进maxVal行比较,如果subSumMax更大则更新最大值maxVal。
maxVal = maxVal>subSumMax?maxVal:subSumMax;
重复进行直至到数组终止。
第三种方法:动态规划 递归实现(不推荐)
class Solution {
public int maxSubArray(int[] nums) {
int maxVal = nums[0];
int subSumMax = 0;
return getMaxVal(nums, subSumMax, maxVal);
}
public int getMaxVal(int []nums, int subSumMax,int maxVal){
if(nums.length == 0){
return 0;
}
if(nums.length == 1){
return nums[0];
}
int i = nums.length - 1;
// maxVal = nums[i];
int []tmpNums = new int[i];
for(int x = 0; x < nums.length -1; x++){
tmpNums[x] = nums[x];
}
subSumMax += nums[i];
subSumMax = subSumMax>nums[i]?subSumMax:nums[i];
int getMax1 = getMaxVal(tmpNums, subSumMax, maxVal);
if(nums.length <=2 && getMax1>0){
subSumMax += getMax1;
subSumMax = subSumMax>getMax1?subSumMax:getMax1;
}
maxVal = maxVal >getMax1?maxVal:getMax1;
maxVal = maxVal>subSumMax?maxVal:subSumMax;
return maxVal;
}
}
大约使用资源情况:
243 ms | 651 MB |
思路:【上面的三目运算可以自己编写max函数 】
假设 i 为数组最后数字的下标, 将nums[i] 与 nums[0 ... i-1] 看成两个部分, 假设 getMax(nums[0 .. i-1]) 是求得左边部分的和,现在只需要比较 目前 三个 数的和, 即 nums[i] 与 nums[i] + nums[i-1] 以及 getMax(nums[0 .. i-1])的最大值。
上面流程是:
首先是确定序列分成两部分 后 右边的数的和 是否大于 累加之前,
即 subSumMax += nums[i];
subSumMax = subSumMax>nums[i]?subSumMax:nums[i];
确定二者之间的最大值。
然后再获取左边部分的 最大值:int getMax1 = getMaxVal(tmpNums, subSumMax, maxVal);
【先略过 <= 2 if语句】
最大值初始是 数组中下标最大的数。
当前最大值 与 左边部分的最大值 进行 比较 更新最大值 :
maxVal = maxVal >getMax1?maxVal:getMax1;
然后 与左边比较后, 还要与右边进行比较更新最大值:
maxVal = maxVal>subSumMax?maxVal:subSumMax;
最后 if 语句判断 <=2 的情况
判断 subSumMax加上getMax1之后和 getMax1的大小,并取最大值。
之所以不推荐这种解法,是因为会爆内存。我提交了五次 也就只通过了两次,其余均超出时间限制。 这只是我个人的一点点偏执,想看看递归的解法,另外上面的递归法还可以进一步优化,不过我应该不会再优化了。
参考:最大连续子序列-分治思想