算法专题--动态规划 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

相关文章
相关标签/搜索