5_53.最大子序和
题目描述:

解题思路:
-
动态规划法:
我们用 sums[i] 代表以第 i个数结尾的「连续子数组的最大和」,那么很显然我们要求的答案就是max(sums[i-1])+nums[i],nums[i]),这样就可以通过维护一个一维数组sums[i]来记录所有的以i为右端点的子序和。 -
优化的动态规划法
分析可知,由于sums[i]只和sums[i-1]有关,因此在计算maxValue的过程中,就会有很多冗余的数据,比如此时i = 7,那么i = 0 ~ 5 这些个nums[i]都是没有必要维持的。因此只需要一个pre变量,来维护对于当前nums[i]的nums[i-1]的值。
对于pre变量的分析,可以先分析这个一维数组,有哪些数据是冗余的,在遍历循环的时候i++,pre就失效了,因此可以移动pre用来节省空间。 -
分治法:
我们定义一个操作get(a, l, r)表示查询 a 序列[l,r]区间内的最大子段和,那么最终我们要求的答案就是get(nums, 0, nums.size() - 1)。
分治的思想就是,先将这些分解成一个个的子空间,当分解到很小的子空间,这个子空间能解决问题了的时候,再对这些进行归并。此题就可以每次分一半,直到最后每个区间只剩下一个元素的时候,自然就是最优的。
问题是:要维护这个区间的哪些信息,并且如何合并呢?
对于一个区间 [l,r],我们可以维护四个量:
lSum 表示 [l,r] 内以 l 为左端点的最大子段和
rSum 表示 [l,r] 内以 r 为右端点的最大子段和
mSum 表示 [l,r] 内的最大子段和
iSum 表示 [l,r] 的区间和
然后通过左右子区间这些信息,来合并。
- 扫描法:
当我们加上一个正数时,和会增加;当加上一个负数时,和会减少。
如果当得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然会减少接下来的和。
代码:
动态规划法
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
int[] sums = new int[len];
sums[0] = nums[0];
int maxValue = sums[0];
for (int i = 1; i < len; i++){
sums[i] = Math.max(nums[i],nums[i] + sums[i - 1]);
maxValue = Math.max(sums[i],maxValue);
}
return maxValue;
}
}
优化的动态规划
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0;
int maxValue = nums[0];
for (int i = 0; i < nums.length; i++){
pre = Math.max(nums[i],pre + nums[i]);
maxValue = Math.max(pre,maxValue);
}
return maxValue;
}
}
分治法
class Solution {
public int maxSubArray(int[] nums) {
int left = 0;
int right = nums.length - 1;
return divide(nums,left,right).mSum;
}
public class Status{
private int lSum,rSum,iSum,mSum;
public Status(int lSum,int rSum,int mSum,int iSum){
this.lSum = lSum;
this.rSum = rSum;
this.mSum = mSum;
this.iSum = iSum;
}
}
public Status divide(int[] nums,int left,int right){
if (left == right){
return new Status(nums[left],nums[left],nums[left],nums[left]);
}
int m = (left + right) >> 1;
Status lSub = divide(nums,left,m);
Status rSub = divide(nums,m + 1,right);
return merge(lSub,rSub);
}
public Status merge(Status left,Status right){
int iSum = left.iSum + right.iSum;
int lSum = Math.max(left.lSum,left.iSum + right.lSum);
int rSum = Math.max(right.rSum,left.rSum + right.iSum);
int mSum = Math.max(Math.max(left.mSum,right.mSum),left.rSum + right.lSum);
return new Status(lSum,rSum,mSum,iSum);
}
}
扫描法
class Solution {
public int maxSubArray(int[] nums) {
int sum = 0;
int maxValue = nums[0];
for (int i = 0; i < nums.length; i++){
if (sum + nums[i] < 0){
sum = 0;
maxValue = Math.max(maxValue,nums[i]);
}else{
sum = sum + nums[i];
maxValue = Math.max(sum,maxValue);
}
}
return maxValue;
}
}
滴水穿石、燕子衔泥,点点滴滴都是添补

浙公网安备 33010602011771号