动态优化: 最大子序号和

1. 53. 最大子序和

Difficulty: 简单

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

  • 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
  • 可以化成’如何求解股票利益最大化’的题(算法导论)

2. Solution

2.1. 分治法求解

  • 有最优子结构: 原问题的最优解包含缩小规模子问题的最优解
class Solution {
    public int maxSubArray(int[] nums) {
    	// 最优子串可能在左串 可能在右串 可能是包含中间元素的中间串
        return recur(nums,0,nums.length-1);
    }
    // 递归 分治法
	private int recur(int[] nums, int l, int r) {
		if(l==r) return nums[l];// 只有一个元素的时候
		int mid=(l+r)/2;
		// 对左中右取最大  
		int leftSum=recur(nums,l,mid);
		int rightSum=recur(nums,mid+1,r);
		// cross的计算不是原问题规模更小的问题 是合并的一部分
		int crossSum=midSum(nums, l, r, mid);
		int res=Math.max(leftSum, Math.max(rightSum, crossSum));
		return res;
	}
	
	// 求中间子串: 这个求和不是原问题的子问题(必须 跨越中点) 所以不用recur()计算
	private int midSum(int[] nums, int l, int r,int mid) {
		if(l==r) return nums[l];// 只有一个元素
		int sumTmp=0,leftSum=Integer.MIN_VALUE;
		int rightSum=leftSum;
		for(int i=mid;i!=-1;i--) {
			sumTmp+=nums[i];
			leftSum=Math.max(sumTmp, leftSum);
		}
		sumTmp=0;
		for(int i=mid+1;i!=nums.length;i++) {
			sumTmp+=nums[i];
			rightSum=Math.max(sumTmp, rightSum);
		}
		return leftSum+rightSum;
	}
}

动态优化解法

class Solution {
    private int[] iToLeft;
	private int[] nums;
	public int maxSubArray(int[] nums) {
		if(nums==null||nums.length==0) return 0;
    	// 记录以i为右子序列端点的最大累和
		this.nums=nums;
        iToLeft=new int[nums.length];
        iToLeft[0]=nums[0];
        for(int i=1;i!=iToLeft.length;i++) {
        	// 要么包含以i-1位右端点的最大累和,要么从i重新开始
        	iToLeft[i]=Math.max(iToLeft[i-1]+nums[i],nums[i]);
        }
        //动态优化: 递归缩减范围
        int res = recur(nums.length-1);
        return res;
    }
	private int recur(int n) {
		if(n==0) return iToLeft[0];
		int res = Math.max(recur(n-1), iToLeft[n]);
		return res;
	}
}

2.2. 优化空间复杂度为O(1)

class Solution {
	public int maxSubArray(int[] nums) {
		int len = nums.length;
		int currSum = nums[0]; // 每一步的当前最优解
		int maxSum = nums[0];
		for (int i = 1; i < len; ++i) {
			//记录右靠区间的最大子区间值
			currSum = Math.max(nums[i], currSum + nums[i]);
			// 将当前最优子区间和i之前的最大子区间和比较
			maxSum = Math.max(maxSum, currSum);
		}
		return maxSum;
	}
}

你可能感兴趣的:(java)