動態優化: 最大子序號和

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;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章