算法專題--動態規劃 vs 貪心算法

動態規劃(Dynamic Programming, DP)

  • 在查找有不少重疊子問題的狀況的最優解時有效。
  • 它將問題從新組合成子問題
  • 爲了不屢次解決這些子問題,它們的結果都逐漸被計算並被保存,從簡單的問題直到整個問題都被解決。
  • 所以,動態規劃保存遞歸時的結果,於是不會在解決一樣的問題時花費時間 · · · · · ·

  • 動態規劃只能應用於有最優 子結構的問題。最優子結構的意思是局部最優解能決定全局最優解(對有些問題這個要求並不能徹底知足,故有時須要引入必定的近似)。算法

  • 簡單地說,問題可以分解成子問題來解決數組

  • 通俗一點來說,動態規劃和其它遍歷算法(如深/廣度優先搜索)都是將原問題拆成多個子問題而後求解,他們之間最本質的區別是,動態規劃保存子問題的解,避免重複計算markdown

  • 解決動態規劃問題的關鍵是找到狀態轉移方程,這樣咱們能夠通計算和儲存子問題的解來求解最終問題spa

  • 同時,咱們也能夠對動態規劃進行空間壓縮,起到節省空間消耗的效果。code

  • 在一些狀況下,動態規劃能夠當作是帶有狀態記錄(memoization)的優先搜索orm

  • 動態規劃是自下而上的,即先解決子問題,再解決父問題;遞歸

  • 而用帶有狀態記錄的優先搜索自上而下的,即從父問題搜索到子問題,若重複搜索到同一個子問題則進行狀態記錄,防止重複計算。字符串

  • 若是題目需求的是最終狀態,那麼使用動態搜索比較方便;it

  • 若是題目須要輸出全部的路徑,那麼使用帶有狀態記錄的優先搜索會比較方便。io

貪心算法

  • 對問題求解的時候,老是作出在當前看來是最好的作法

  • 適用貪心算法的場景:問題可以分解成子問題來解決,子問題的最優解能遞推到最終問題的最優解。這種子問題最優解成爲最優子結構

回溯法

回溯法(backtracking)是優先搜索的一種特殊狀況,又稱爲試探法,經常使用於須要記錄節點狀態的深度優先搜索。一般來講,排列、組合、選擇類問題使用回溯法比較方便。 顧名思義,回溯法的核心是回溯。在搜索到某一節點的時候,若是咱們發現目前的節點(及其子節點)並非需求目標時,咱們回退到原來的節點繼續搜索,而且把在目前節點修改的狀態 還原

這樣的好處是咱們能夠始終只對圖的總狀態進行修改,而非每次遍歷時新建一個圖來儲存 狀態。

在具體的寫法上,它與普通的深度優先搜索同樣,都有 [修改當前節點狀態]→[遞歸子節 點] 的步驟,只是多了回溯的步驟,變成了 [修改當前節點狀態]→[遞歸子節點]→[回改當前節點 狀態]

回溯法。有兩個小訣竅,一是按引用傳狀態,二是全部的狀態修 改在遞歸完成後回改。 回溯法修改通常有兩種狀況,一種是修改最後一位輸出,好比排列組合;一種是修改訪問標 記,好比矩陣裏搜字符串。

貪心算法 vs 動態規劃

  • 貪心算法與動態規劃的不一樣在於它對每一個子問題的解決方案都做出選擇,不能回退
  • 動態規劃則會保存之前的運算結果,並根據之前的結果對當前進行選擇,有回退功能
  • 而回溯算法就是大量的重複計算來得到最優解

image.png

動態規劃 -- Leetcode 70. 爬樓梯

假設你正在爬樓梯。須要 n 階你才能到達樓頂。

每次你能夠爬 1 或 2 個臺階。你有多少種不一樣的方法能夠爬到樓頂呢?

注意:給定 n 是一個正整數。

image.png

題解

這是十分經典的斐波那契數列題。定義一個數組 dp,dp[i] 表示走到第 i 階的方法數,走到第 i 階的 方法數即爲走到第 i-1 階的方法數加上走到第 i-2 階的方法數。這樣咱們就獲得了狀態轉移方程 dp[i] = dp[i-1] + dp[i-2]。注意邊界條件的處理。

const climbStairs = function(n) {
     if(n <= 2) return n;
     const dp = [1, 2];
     for(let i = 2; i <= n; i++) {
       dp[i] = dp[i-1] + dp[i-2]
     }
     return dp[n-1];
}
// O(n) 空間複雜度
複製代碼

動態規劃進行空間壓縮 dp[i] 只與 dp[i-1] 和 dp[i-2] 有關,所以能夠只用兩個變量來存儲 dp[i-1] 和 dp[i-2]

const climbStairs = function(n){
  if(n <= 2) return n;
  let pre1 = 1, pre2 = 2, cur;
  for(let i = 2; i < n; i++) {
    cur = pre1 + pre2;
    pre1 = pre2;
    pre2 = cur;
  }
  return cur;
}

複製代碼

遞歸寫法:(能夠無視,空間複雜度最高)

const climbStairs = function(n){
  if(n <= 2) return n;
  return climbStairs(n - 1) + climbStairs(n-2);
}
複製代碼

超時 image.png

相關文章
相關標籤/搜索