【算法題】最大連續子序和

一道 LeetCode 的動態規劃題的分析。算法

題目描述

題目來源 leetCode——53.最大子序和數組

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

示例:bash

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
複製代碼

進階: 若是你已經實現複雜度爲 O(n) 的解法,嘗試使用更爲精妙的分治法求解。數據結構

動態規劃

在分析如何使用動態規劃求解該問題前,咱們先簡單瞭解下什麼是什麼是動態規劃(DP)。函數

動態規劃的適合解決的問題應該符合 「一個模型三個特徵」ui

一個模型

一個模型指的是 多階段決策最優解模型。指的是要經歷多個決策階段,每一個決策對應多個狀態。而後咱們從中找出一組可以產生最優解的決策序列。spa

三個特徵

  1. 最優子結構

後面的決策能夠經過前面的決策推導出。 好比說最經典的0-1揹包問題,咱們依次把物品放入揹包(或不放入)揹包。當進行到第 n 次決策(是否將第 n 個物品放入書包)時,前面的第 n - 1 已經決定好的多種決策(前面不一樣決策序列最終在 n - 1 次決策時給出了全部能夠達到的重量),不會由於第 n 次的決策發生改變。3d

  1. 無後效性

一旦某個階段 a 的決策結束後,後面階段進行決策時,就不用考慮到階段 a 是如何推導出來的。咱們只須要用到它的最終獲得的決策序列。code

  1. 重複子問題

不一樣的決策序列,在到達第 n 次決策結束後,可能會產生重複的狀態。好比假設依次要放入揹包的物品的重量分別爲 二、四、二、3。 咱們進入決策是否放入第3個物品的階段時,就會出現 「放入第一、3個物品和只放入第2個物品的兩組決策序列達到相同的重量」。這就是重複的狀態,可能會發生也可能不會發生。

動態規劃的兩種解題思路

1. 狀態轉移表法

動態規劃能解決的問題,均可以用回溯法的暴力搜索解決。因此咱們能夠先用簡單的回溯算法去試着去解決,從中找到規律,畫出遞歸樹。

當咱們發現重複子問題時,一是可使用 回溯+「備忘錄」 的方法。所謂備忘錄,就是咱們會把 f(n) 的結果用散列表保存起來(回溯常常用到遞歸函數),當又一次調用 f(n) 時,就直接取出緩存起來的結果,以減小重複計算。二是這裏說的 狀態表轉移表法

一般狀態表是二維度的。每行表明着每個階段決策後的多個狀態。這個二維數組會一行一行地進行填充,直到達到知足結束狀態的狀況(好比0-1揹包問題就是重量恰好達到揹包最大承重,或者n個物品都進行了決策,即完成了第 n 次決策)。這時咱們只須要從最終的階段找出最終結果便可(揹包問題是從後往前找出一個重量最大的值)

2. 狀態轉移方程法

關鍵在於找出 狀態轉移方程。根據最優子結構,寫出遞歸公式,也就是所謂的狀態轉移方程。

動態規劃解法

那麼咱們開始着手分析問題了。

首先咱們試着用 狀態轉移表法 來作這道題。

以 [-2,1,-3,4,-1,2,1,-5,4] 爲例進行分析,咱們畫個遞歸樹分析一下。

(i, sum)。i 表明 第 i 個階段的決策,具體作的決策是:是選擇當前的元素爲新的子數組的起點,仍是讓當前元素做爲原來子數組的後繼數組元素。具體邏輯以下圖:

每一個階段咱們都會拿到決策後的兩種連續子數組的和,咱們會把它們和上一次階段的最大和比較,取出最大的值。

這裏要注意的是,當咱們要選擇決策後的兩種狀況的其中一種狀況和大的狀況,進行下一次遍歷,另外一種狀況是不須要進行下次的遞歸的。由於總和小的狀況下的數組,和後面的數組相加時,必然比總和大的狀況的總和要小,因此不須要進行接下來的遞歸。

js 代碼實現

var maxSubArray = function(nums) {
    // 動態規劃
    let len = nums.length;
    let max = nums[0];
    let prevSum = nums[0];

    for (let i = 1; i < len; i++) {
        prevSum = Math.max(nums[i], prevSum + nums[i]);
        max = Math.max(max, prevSum);
    }
    return max;
};
複製代碼

參考

相關文章
相關標籤/搜索