經典的最大子序列和

題目描述

給定一個整數數組,找到一個連續子數組其元素之和最大並返回
Input:nums
Output:最大連續子數組之和

三種解法,分別是動態規劃、貪心法、分治法,其中分治算法不是最優的。

1. 動態規劃

定義dp[i]dp[i]:以位置ii爲結尾的子數組最大和
dp[i+1]={dp[i]+nums[i],dp[i]>0nums[i],dp[i+1] = \begin{cases} dp[i] + nums[i], dp[i]>0 \\ nums[i], 其他 \end{cases}
  需要遍歷數組一次,時間複雜度爲O(n),空間複雜度爲O(1)。缺點是直接在nums上操作,會改變原來的數組nums。

	/**
     * 動態規劃
     * @param nums
     * @return
     */
    public int maxSubArrayDynamic(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int maxSum = nums[0];
        for(int i = 1;i<nums.length;i++){
            nums[i] += nums[i-1] > 0 ? nums[i-1] : 0;
            maxSum = Math.max(maxSum, nums[i]);
        }
        return maxSum;
    }

2. 貪心法

  curSum用於記錄當前和(每一步都選擇最優的方案)
  僅僅需要遍歷數組一次,時間複雜度爲O(n),空間複雜度爲O(1)。

	/**
     * 貪心
     * @param nums
     * @return
     */
    public int maxSubArrayGreed(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int curSum = nums[0], maxSum = curSum;
        for(int i = 1;i<nums.length;i++){
            curSum = Math.max(curSum + nums[i], nums[i]);
            maxSum = Math.max(maxSum, curSum);
        }
        return maxSum;
    }	

3. 分治

  首先將數組劃分成左和右兩部分,具有最大和的連續子數組的位置可能有如下三種情況:

  1. 位於左邊的數組部分;
  2. 位於右邊的數組部分;
  3. 位於左和右交叉的部分,即最大子數組有一部分在左邊,一部分在右邊。

其中,第一種情況和第二種情況可以遞歸。第三種情況由函數crossSum暴力計算,需要遍歷數組一次,使得時間複雜度需要乘O(n)

    /**
     * 暴力計算包含位置p在內的最大子數組和
     * @param nums
     * @param left
     * @param right
     * @param p
     * @return
     */
    private int crossSum(int[] nums, int left, int right, int p){
        if(left == right)   return nums[left];

        int leftSubsum = Integer.MIN_VALUE;
        int rightSubsum = Integer.MIN_VALUE;

        int curSum = 0;
        for(int i = p;i >= left;i--){
            curSum += nums[i];
            leftSubsum = Math.max(leftSubsum, curSum);
        }
        curSum = 0;
        for(int i = p+1;i<=right;i++){
            curSum += nums[i];
            rightSubsum = Math.max(rightSubsum, curSum);
        }
        return leftSubsum + rightSubsum;
    }
    private int helper(int[] nums, int left, int right){
        if(left == right) return nums[left];
        int p = (left + right) >>> 1;
        int leftSum = helper(nums, left, p);
        int rightSum = helper(nums, p+1, right);
        int crossSum = crossSum(nums, left, right, p);
        return Math.max(crossSum, Math.max(leftSum, rightSum));
    }
    /**
     * 分治
     * @param nums
     * @return
     */
    public int maxSubArrayDivide(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        return helper(nums, 0, nums.length-1);
    }

時間複雜度爲O(nlogn),helper函數多次遞歸,造成了空間複雜度爲O(logn)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章