AlgoMooc
你已开通华为OD训练营权益,还差最后一步——完成入营激活(兑换课程 + 加飞书 + 登记服务群),即可解锁全部课程与专属服务。去激活 →
← OD 20 天课程

华为 OD 训练营 · 题解精讲

LC213. 打家劫舍II

LC213. 打家劫舍II

题目描述

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。 给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。 示例 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: 输入:nums = [0] 输出:0 提示: 1 <= nums.length <= 100 0 <= nums[i] <= 1000

题目解析

解题思路

本题是“打家劫舍”的环形版本,核心难点在于首尾房间不能同时被偷。参考代码采用分类讨论策略,将环形问题拆解为两个线性子问题:要么不偷第一个房间(考虑下标1到n-1),要么不偷最后一个房间(考虑下标0到n-2)。最终答案取这两个子问题结果的最大值。

算法与数据结构:动态规划(DP),使用一维数组value记录前i个房间能偷到的最大金额。

关键步骤

1. 边界处理:若数组长度n<=1,直接返回对应值。 2. 拆分数组:生成两个子数组——nums[1:](排除第一个房间)和nums[:-1](排除最后一个房间)。 3. 调用线性DP函数myRob

  • 初始化value[0]=0value[1]=nums[0]
  • i=2n,状态转移方程:

     value[i] = max(value[i-1], value[i-2] + nums[i-1])
  • 含义:当前房间i-1要么不偷(继承value[i-1]),要么偷(加上value[i-2])。

4. 取最大值:返回两个子问题结果中的较大值。

示意图(以nums=[1,2,3,1]为例):


情况1(排除首房):[2,3,1] → DP得4
情况2(排除尾房):[1,2,3] → DP得4
最终结果:max(4,4)=4

复杂度分析

  • 时间复杂度:O(n),每个子数组各执行一次线性DP,总遍历次数为2n。
  • 空间复杂度:O(n),DP数组value长度为n+1。可优化为O(1)(但参考代码未做此优化)。

参考代码

class Solution:
    def rob(self, nums):
        # 获取房间总数
        n = len(nums)

        # 如果数组为空,返回0
        if n == 0:
            return 0

        # 如果数组只有一个元素,返回该元素的金额
        if n == 1:
            return nums[0]

        # 不选择第一个房间
        nums_except_first_room = nums[1:]

        # 不选择最后一个房间
        nums_except_last_room = nums[:-1]

        # 从这两个选择中挑选出最大值
        return max(self.myRob(nums_except_first_room), self.myRob(nums_except_last_room))

    def myRob(self, nums):
        # 获取房间总数
        n = len(nums)

        # 创建一个数组value来存放前n个房间可以偷取的最大金额
        value = [0] * (n + 1)

        # 前0个房间无法偷取,金额为0
        value[0] = 0

        # 前1个房间只有一个房间可偷取,金额为该房间的金额
        value[1] = nums[0]

        # 从i = 2到i = n,value中的结果由前i-2和i-1共同决定
        for i in range(2, n + 1):
            # 转移方程:value[i]等于value[i-1]和value[i-2]+nums[i-1]中的较大值
            value[i] = max(value[i - 1], value[i - 2] + nums[i - 1])

        # 返回value的最后一个值
        return value[n]