解法一:
核心思路:动态规划
假设数组 nums 的值全都严格大于 0,那么一定有 dp[i] = dp[i - 1] + nums[i]。但是 dp[i - 1] 有可能是负数,所以需要分类讨论:
(1)如果 dp[i - 1] > 0,那么可以把 nums[i] 直接接在 dp[i - 1] 表示的那个数组的后面,得到和更大的连续子数组;
(2)如果 dp[i - 1] <= 0,那么 nums[i] 加上前面的数 dp[i - 1] 以后值不会变大。于是 nums[i] 此时自己就是最大值,也就是 dp[i]。
总结:
这道题的动态规划的思路与之前的题,是不太一样的。之前的动态规划的题目在解题过程中,是已经选好了dp[i-1],然后来考虑要不要选第i个值。但是这道题是一定要选第i个值,然后再考虑要不要选第dp[i-1]。学到了学到了!!!
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0;
//这里为什么不等于0,因为nums=[-1]时,答案错误
int max_sub = nums[0];
for(int num:nums){
pre = Math.max(pre + num , num);
max_sub = Math.max(max_sub , pre);
}
return max_sub;
}
}
解法二:
核心思路:分治法–线段树
class Solution {
public int maxSubArray(int[] nums) {
return getBestSum(nums,0,nums.length-1).m_Sum;
}
public STree getBestSum(int[] nums,int l,int r){
if(l == r){
return new STree(nums[l],nums[l],nums[l],nums[l]);
}
int mid = (l + r) >> 1;
STree l_stree = getBestSum(nums,l,mid);
STree r_stree = getBestSum(nums,mid+1,r);
return update_STree(l_stree,r_stree);
}
public STree update_STree(STree l_stree, STree r_stree){
int i_Sum = l_stree.i_Sum + r_stree.i_Sum;
//仔细看l_Sum、r_Sum、m_Sum的取值,相当精髓
int l_Sum = Math.max(l_stree.l_Sum,(l_stree.i_Sum + r_stree.l_Sum));
int r_Sum = Math.max(r_stree.r_Sum,(r_stree.i_Sum + l_stree.r_Sum));
int m_Sum = Math.max(Math.max(l_stree.m_Sum,r_stree.m_Sum),(l_stree.r_Sum+r_stree.l_Sum));
return new STree(l_Sum,r_Sum,i_Sum,m_Sum);
}
}
public class STree{
int l_Sum; //在[l,r]中,以左边第一个元素开头最大连续和
int r_Sum; //在[l,r]中,以右边第一个元素开头最大连续和
int i_Sum; //在[l,r]中,整段序列之和
int m_Sum; //在[l,r]中,整段序列之中最大的连续和
public STree(int l_Sum,int r_Sum,int i_Sum,int m_Sum){
this.l_Sum = l_Sum;
this.r_Sum = r_Sum;
this.i_Sum = i_Sum;
this.m_Sum = m_Sum;
}
}
此方法可用于求解任何区间内的最大子段和,本题设为[0,length-1]。