經典算法解 · 最大子序合(貪心·分治·動態規劃)

部分轉載:LeetCode題解
——————————————————

題目:最大子序合

給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6

解釋: 連續子數組 [4,-1,2,1]的和最大,爲6。

方法一:貪心算法

  • 使用單個數組作爲輸入來查找最大(或最小)元素(或總和)的問題,貪心算法是可以在線性時間解決的方法之一。
  • 每一步都選擇最佳方案,到最後就是全局最優的方案。

var maxSubArray = function(nums) {
    let ans = nums[0];
    let current = nums[0];
    for(let i = 1; i < nums.length; i++) {
        current = Math.max(nums[i], current+nums[i]);
        ans = Math.max(current, ans);  
    };
    return ans;
};

  • 時間複雜度:O(N)。只遍歷一次數組。
  • 空間複雜度:O(1)。只遍歷一次數組。

方法二:分治法

這個是使用分治法解決問題的典型的例子,並且可以用與合併排序相似的算法求解。下面是用分治法解決問題的模板:

  • 定義基本情況。
  • 將問題分解爲子問題並遞歸地解決它們。
  • 合併子問題的解以獲得原始問題的解。

算法

當最大子數組有 n 個數字時:
若 n==1,返回此元素。
left_sum 爲最大子數組前 n/2 個元素,在索引爲(left + right) / 2的元素屬於左子數組。
right_sum 爲最大子數組的右子數組,爲最後 n/2 的元素。
cross_sum 是包含左右子數組且含索引(left + right) / 2 的最大值。

在這裏插入圖片描述


var maxSubArray = function(nums) {
    const cross_sum = (nums, left, right, p)=> {
        if (left == right){return nums[left]}
        let curr_sum1 = 0;
        let left_subsum = Number.NEGATIVE_INFINITY;
        let right_subsum = Number.NEGATIVE_INFINITY;
        for(let i = p;i>left-1; i--){
            curr_sum1 += nums[i];
            left_subsum = Math.max(left_subsum, curr_sum1);
        }
        let curr_sum2 = 0
        for(let i = p+1;i<right+1; i++){
        curr_sum2 += nums[i];
        right_subsum = Math.max(right_subsum, curr_sum2);
        }
        return left_subsum + right_subsum;
    }
            
    const helper = (nums, left, right)=>{
        if (left == right) return nums[left];
        let p = Math.floor((left + right) / 2);
        let left_sum = helper(nums, left, p);
        let right_sum = helper(nums, p + 1, right);
        let crossSum = cross_sum(nums, left, right, p);
        return Math.max(left_sum, right_sum, crossSum);
    }
        
    return helper(nums, 0, nums.length - 1);
};

  • 時間複雜度:O(NlogN)。
  • 空間複雜度:O(logN),遞歸時棧使用的空間。

動態規劃(Kadane 算法)

算法

在整個數組或在固定大小的滑動窗口中找到總和或最大值或最小值的問題可以通過動態規劃(DP)在線性時間內解決。

有兩種標準 DP 方法適用於數組:

  • 常數空間,沿數組移動並在原數組修改。
  • 線性空間,首先沿 left->right方向移動,然後再沿 right->left 方向移動。 合併結果。

我們在這裏使用第一種方法,因爲可以修改數組跟蹤當前位置的最大和。
下一步是在知道當前位置的最大和後更新全局最大和。


var maxSubArray = function(nums) {
    let ans = nums[0];
    let sum = 0;
    for(const num of nums) {
        if(sum > 0) {
            sum += num;
        } else {
            sum = num;
        }
        ans = Math.max(ans, sum);
    }
    return ans;
  }

  • 時間複雜度:O(N)。只遍歷一次數組。
  • 空間複雜度:O(1)。只遍歷一次數組。
發佈了42 篇原創文章 · 獲贊 34 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章