前端算法必刷题系列[90]

254 阅读3分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

164. 打家劫舍 (house-robber)

标签

  • 中等
  • 动态规划

题目

leetcode 传送门

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4

示例 2

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12

基本思路

偷到最多钱,其实就是个背包问题,遇到一家,选择偷或不偷,跟选物品放入背包类似。

使用动态规划,还是那3步

状态定义

我们的目标是偷到的钱最多

那么就设 dp[i] 就是偷前面 i 间,可以偷到的最高金额

状态转移

i 号房门口,我们可以选择偷,或者不偷,不是良心发现,是利益最大化

  • 偷 => dp[i] = dp[i-2] + nums[i]
  • 不偷 => dp[i] = dp[i-1]

那么我们可轻易得出: dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1])

边界

基本都是从 0 开始考虑问题边界

  • dp[0] = 0

我们为了语义,其实第 0 间房是不存在的,空出数组下标,让下标 1对应第一间房

  • dp[1] = nums[0]

第一间房,最大的话肯定就是这个数了

写法实现

var rob = function(nums) {
  let n = nums.length
  if (!n) return 0
  // 为了语义化,让下标和房号对应
  let dp = new Array(n + 1).fill(0)
  dp[1] = nums[0];
  for (let i = 2; i <= n; i++) {
    dp[i] = Math.max(dp[i-2] + nums[i-1], dp[i-1])
  }
  return dp[n]
};

165. 打家劫舍 II (house-robber-ii)

标签

  • 中等
  • 动态规划

题目

leetcode 传送门

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),
然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4

基本思路

这题跟上题类似,但是不同的是,房子头尾相连成

使用动态规划,还是那3步,等等,我们能先分解这个问题吗,如果没做上面一道题,那我们就开始分析3步了,然后在状态转移那块再讨论,但做完上面一题,我们应该天然想到,本题是可以分治的。

头尾拆成两种情况考虑,选头还是选尾。

  • 选头 就是 [0 .. n-1] 用 第一题方案得出 一个 max_with_head
  • 选尾 就是 [1 .. n] 用 第一题方案得出 一个 max_with_tail
  • 然后比较大小

写法实现

我觉得这个已经非常语义化了,基本不用多注释

var rob = function(nums) {
  // 确实只有一个,你不能跳过,直接返回金额就行
  if (nums.length === 1) return nums[0]
  const robNoCircle = function(nums) {
    let n = nums.length
    if (!n) return 0
    let dp = new Array(n + 1).fill(0)
    dp[1] = nums[0];
    for (let i = 2; i <= n; i++) {
      dp[i] = Math.max(dp[i-2] + nums[i-1], dp[i-1])
    }
    return dp[n]
  };
  let maxWithHead = robNoCircle(nums.slice(0, nums.length-1))
  let maxWithTail = robNoCircle(nums.slice(1))
  return Math.max(maxWithHead, maxWithTail)
};

另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考