【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 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

解题思路

  1. 状态定义 :用prev_prev记录 "偷到前前间房时的最大金额",prev记录 "偷到前一间房时的最大金额"(替代传统的dp数组,优化空间复杂度至 O (1))。

  2. 状态转移 :对于第i间房,有两种选择:

    • 不偷当前房 :最大金额等于 "偷到前一间房的金额"(即prev);
    • 偷当前房 :最大金额等于 "偷到前前间房的金额 + 当前房的金额"(即prev_prev + nums[i]);取两者的较大值作为当前房的最大金额,并更新前序状态。
  3. 边界处理

    • 空数组直接返回 0;
    • 只有 1 间房时,直接返回该房的金额。

示例验证

  • 示例 1:输入[1,2,3,1]遍历过程:

    • 初始:prev_prev=1prev=2
    • 第 3 间房(值 3):current = max(2, 1+3=4)prev=4
    • 第 4 间房(值 1):current = max(4, 2+1=3) → 最终返回4
  • 示例 2:输入[2,7,9,3,1]遍历过程:

    • 初始:prev_prev=2prev=7
    • 第 3 间房(值 9):current = max(7, 2+9=11)prev=11
    • 第 4 间房(值 3):current = max(11, 7+3=10)prev=11
    • 第 5 间房(值 1):current = max(11, 11+1=12) → 最终返回12

Python代码:

python 复制代码
from typing import List


class Solution:
    def rob(self, nums: List[int]) -> int:
        """
        计算不触动警报时能偷窃的最高金额(空间优化版:O(1)空间复杂度)
        :param nums: 非负整数数组,每个元素代表对应房屋的现金金额
        :return: 能偷窃的最高金额
        """
        # 边界条件1:空数组(题目提示长度≥1,此判断为鲁棒性补充)
        if not nums:
            return 0
        # 边界条件2:只有1间房,直接偷这间
        if len(nums) == 1:
            return nums[0]

        # 状态初始化:
        # prev_prev: 偷到「前前间房」时的最大金额(对应dp[i-2])
        # prev: 偷到「前一间房」时的最大金额(对应dp[i-1])
        prev_prev = nums[0]
        prev = max(nums[0], nums[1])  # 前两间房选金额大的偷

        # 从第3间房开始遍历(索引2),逐间推导最大金额
        for i in range(2, len(nums)):
            # 状态转移核心:
            # 偷当前房:不能偷前一间,金额=prev_prev + 当前房金额
            # 不偷当前房:金额=prev(前一间的最大金额)
            current = max(prev, prev_prev + nums[i])

            # 更新前序状态:为下一间房的计算做准备
            prev_prev, prev = prev, current

        # 最终prev存储的是偷到最后一间房时的最大金额
        return prev

    def rob_dp_array(self, nums: List[int]) -> int:
        """
        传统DP数组版(O(n)空间复杂度,更易理解)
        :param nums: 房屋金额数组
        :return: 最高偷窃金额
        """
        if not nums:
            return 0
        n = len(nums)
        if n == 1:
            return nums[0]

        # dp[i]:偷到第i间房(索引i)时的最大金额
        dp = [0] * n
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])

        for i in range(2, n):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])

        return dp[-1]


# ---------------------- 测试用例 ----------------------
if __name__ == "__main__":
    solution = Solution()

    # 测试示例1
    nums1 = [1, 2, 3, 1]
    res1 = solution.rob(nums1)
    print(f"示例1输入:{nums1}")
    print(f"示例1输出(空间优化版):{res1}(预期:4)")
    print(f"示例1输出(DP数组版):{solution.rob_dp_array(nums1)}(预期:4)")
    print("-" * 30)

    # 测试示例2
    nums2 = [2, 7, 9, 3, 1]
    res2 = solution.rob(nums2)
    print(f"示例2输入:{nums2}")
    print(f"示例2输出(空间优化版):{res2}(预期:12)")
    print(f"示例2输出(DP数组版):{solution.rob_dp_array(nums2)}(预期:12)")
    print("-" * 30)

    # 额外测试:单元素数组
    nums3 = [5]
    res3 = solution.rob(nums3)
    print(f"额外测试(单元素):{nums3} → {res3}(预期:5)")

    # 额外测试:两元素数组
    nums4 = [3, 7]
    res4 = solution.rob(nums4)
    print(f"额外测试(两元素):{nums4} → {res4}(预期:7)")

LeetCode提交代码:

python 复制代码
from typing import List

class Solution:
    def rob(self, nums: List[int]) -> int:
        # 处理空数组或单元素数组的特殊情况
        if not nums:
            return 0
        if len(nums) == 1:
            return nums[0]
        
        # 初始化前两个状态:prev_prev对应"前前间房的最大金额",prev对应"前一间房的最大金额"
        prev_prev = nums[0]
        prev = max(nums[0], nums[1])
        
        # 从第3间房开始遍历(索引为2)
        for i in range(2, len(nums)):
            # 当前房的最大金额 = max(不偷当前房(取前一间的金额), 偷当前房(取前前间+当前房的金额))
            current = max(prev, prev_prev + nums[i])
            # 更新前序状态
            prev_prev, prev = prev, current
        
        return prev

程序运行结果展示:

bash 复制代码
示例1输入:[1, 2, 3, 1]
示例1输出(空间优化版):4(预期:4)
示例1输出(DP数组版):4(预期:4)
------------------------------
示例2输入:[2, 7, 9, 3, 1]
示例2输出(空间优化版):12(预期:12)
示例2输出(DP数组版):12(预期:12)
------------------------------
额外测试(单元素):[5] → 5(预期:5)
额外测试(两元素):[3, 7] → 7(预期:7)

总结

本文解决房屋盗窃问题,要求在不相邻房屋中获取最高金额。采用动态规划方法,定义状态prev_prevprev分别表示前前间和前一间的最大金额。状态转移方程为current = max(prev, prev_prev + nums[i]),优化空间复杂度至O(1)。示例验证显示算法正确性,如输入[1,2,3,1]输出4,输入[2,7,9,3,1]输出12。Python代码实现包含空间优化版和传统DP数组版,边界处理覆盖空数组和单元素情况。运行结果与预期一致,证明算法有效性。

相关推荐
李昊哲小课2 小时前
简化版天气爬虫教程
爬虫·python
3824278272 小时前
python:Ajax爬取电影详情实战
开发语言·python·ajax
冰西瓜6002 小时前
STL——vector
数据结构·c++·算法
天呐草莓2 小时前
集成学习 (ensemble learning)
人工智能·python·深度学习·算法·机器学习·数据挖掘·集成学习
努力学算法的蒟蒻2 小时前
day45(12.26)——leetcode面试经典150
算法·leetcode·面试
BBB努力学习程序设计2 小时前
Python多线程与多进程编程实战指南
python
雪落无尘处2 小时前
Anaconda 虚拟环境配置全攻略+Pycharm使用虚拟环境开发:从安装到高效管理
后端·python·pycharm·conda·anaconda
闻缺陷则喜何志丹2 小时前
【离线查询 前缀和 二分查找 栈】P12271 [蓝桥杯 2024 国 Python B] 括号与字母|普及+
c++·算法·前缀和·蓝桥杯·二分查找··离线查询
Bdygsl2 小时前
数据结构 —— 双向循环链表
数据结构·链表