谈一谈动态规划和dfs

前言 前端学算法还是有必要性的,不说现在大厂面试都考算法,就是做权限模块的时候,如果不懂点算法,写树结构的自定义遍历都写不出来。
今天把最近刷题的遇到的动态规划和dfs做一个总结,希望对大家学习算法能有所帮助。
DP和dfs关系 动态规划(dp):

  • 是从下往上的一个遍历。
  • 需要一个递推公式比如f(n)=f(n-1)+f(n-2)这种。
  • 不需要递归只需要普通的迭代,所以性能好。
深度优先搜索(dfs):
  • 是从上往下的一个递归。
  • 也需要一个递推公式比如f(n)=f(n-1)+f(n-2)这种。
  • 从上往下递归遇到终止条件后又会不断的从下往上取值(递归本质),过程类似dp
  • 由于遍历所有可能性,所以性能特别差,一般会剪枝
  • 一般剪枝手法就是按条件提前终止或者记忆化
联系
  • 记忆化后的dfs其实本质上和dp差不多
  • 因为两者都要递推公式,所以理论上dfs能解的题dp都能解
  • 不过有的题适合dfs,比如求解可能性的题目
一道经典题目双解
leetcode322. 兑换零钱
dp
var coinChange = function(coins, amount) { /** 遍历i dp(n) = min( dp(n-conin[i]) + 1) */ coins.sort((a,b)=>b-a) const arr = new Array(amount+1).fill(0) for(let i =1; i<=amount; i++){ let curAmount = i //总额 for(let j=0; j

dfs
不剪枝会超时,用缓存去剪枝,剪枝了一般效率还是打不过Dp,除非有非常好的剪枝策略
var coinChange = function(coins, amount) { coins.sort((a,b)=>b-a) let cache = {} const dfs = (restamount)=>{if(restamount===0) return 0 if(restamount

适合dsf的题目
leetcode46. 全排列
求解可能性的题目,非常适合用dfs.
如果用dp,代码会很难组织
var permute = function(nums) { //dfs /** dp(n) = dp(n-1)数组中每个元素在i位置(0<=i<=n)添加n */ const dfs=(nums)=>{ // [1,2] if(nums.length===1) return [nums.slice(0,1)] const arr = dfs(nums.slice(0,-1)) const res = [] arr.forEach(item=>{ for(var i=0; i<=item.length; i++){ let addedItem = item.slice() addedItem.splice(i,0,nums[nums.length-1]) res.push(addedItem)} }) return res } return dfs(nums) };

适合dp的题目
leetcode198. 打家劫舍
【谈一谈动态规划和dfs】这个题目用dp解就很清爽,当然递推公式包含贪心思想。
var rob = function(nums) { //dp(n) = max(dp(n-2)+nums[n] , dp(n-1))总共有N家,贪心。从左往右轮到第N家的时候的收益const dp = new Array(nums.length).fill(0) dp[1] = nums[0] for(let i=2; i<=nums.length; i++){ dp[i] = Math.max(dp[i-2]+nums[i-1] , dp[i-1]) //轮到第i家的收益 }return dp[nums.length] };

    推荐阅读