当前位置: 首页 > news >正文

【力扣100题】43.打家劫舍

题目描述

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

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

示例 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

解题思路总览

方法思路时间复杂度空间复杂度适用场景
方法一:动态规划dp[i] = max(dp[i-2] + nums[i], dp[i-1]),滚动数组优化O(n)O(1)面试首选
方法二:空间未优化版标准的 DP 数组O(n)O(n)容易理解

核心原理:偷到第 i 间房时,最大收益 = max(偷第 i 间:dp[i-2] + nums[i],不偷第 i 间:dp[i-1])


方法一:动态规划(推荐)

思路

经典的房间 DP 问题:

  1. 状态定义dp[i]表示偷到第 i 间房(0-indexed)时能获取的最高金额
  2. 状态转移
    • 偷第 i 间房:不能偷 i-1 间,所以最大收益是dp[i-2] + nums[i]
    • 不偷第 i 间房:最大收益是dp[i-1]
    • dp[i] = max(dp[i-2] + nums[i], dp[i-1])
  3. 初始化dp[0] = nums[0]dp[1] = max(nums[0], nums[1])
  4. 空间优化:只需要前两个状态,用变量滚动

完整代码

classSolution{public:introb(vector<int>&nums){if(nums.size()==0)return0;intn=nums.size();if(n==1)returnnums[0];vector<int>dp(n,0);dp[0]=nums[0];dp[1]=max(nums[0],nums[1]);for(inti=2;i<n;i++){dp[i]=max(dp[i-2]+nums[i],dp[i-1]);}returndp[n-1];}};

算法流程图

nums = [2,7,9,3,1]为例:

初始: dp[0] = 2 dp[1] = max(2, 7) = 7 i = 2: dp[2] = max(dp[0] + nums[2], dp[1]) = max(2 + 9, 7) = max(11, 7) = 11 说明:偷第2间房(9)收益更高 i = 3: dp[3] = max(dp[1] + nums[3], dp[2]) = max(7 + 3, 11) = max(10, 11) = 11 说明:不偷第3间房,保留之前的11 i = 4: dp[4] = max(dp[2] + nums[4], dp[3]) = max(11 + 1, 11) = max(12, 11) = 12 说明:偷第4间房得到最高收益12 最终 dp[4] = 12 偷窃方案:房间0(2) + 房间2(9) + 房间4(1) = 12

逐行解析

if(nums.size()==0)return0;
  • 空数组返回 0。
intn=nums.size();if(n==1)returnnums[0];
  • 只有一间房,直接返回。
vector<int>dp(n,0);dp[0]=nums[0];dp[1]=max(nums[0],nums[1]);
  • 初始化:
    • dp[0] = nums[0],只有一间房只能偷它
    • dp[1] = max(nums[0], nums[1]),两间房不能同时偷,选金额大的
for(inti=2;i<n;i++){dp[i]=max(dp[i-2]+nums[i],dp[i-1]);}
  • 核心状态转移
    • dp[i-2] + nums[i]:偷第 i 间房,加上之前偷到 i-2 的最大收益
    • dp[i-1]:不偷第 i 间房,保留之前的最大收益
    • 取两者的最大值
returndp[n-1];
  • 返回最后一间房的 dp 值,即全局最大收益。

复杂度分析

复杂度说明
时间O(n)遍历一遍数组
空间O(n)DP 数组

优点:状态转移清晰,空间可优化
缺点:


空间优化版本

思路

只需要保存前两个状态,将空间复杂度降到 O(1)。

完整代码

classSolution{public:introb(vector<int>&nums){if(nums.empty())return0;intn=nums.size();if(n==1)returnnums[0];intprev2=nums[0];// dp[i-2]intprev1=max(nums[0],nums[1]);// dp[i-1]for(inti=2;i<n;i++){intcur=max(prev2+nums[i],prev1);// dp[i]prev2=prev1;prev1=cur;}returnprev1;}};

逐行解析

intprev2=nums[0];// dp[i-2]intprev1=max(nums[0],nums[1]);// dp[i-1]
  • prev2存储dp[i-2]
  • prev1存储dp[i-1]
for(inti=2;i<n;i++){intcur=max(prev2+nums[i],prev1);// dp[i]prev2=prev1;prev1=cur;}
  • 计算当前dp[i]
  • 更新prev2 = prev1(dp[i-1] 变成下一轮的 dp[i-2])
  • 更新prev1 = cur(dp[i] 变成下一轮的 dp[i-1])

复杂度分析

复杂度说明
时间O(n)遍历一遍数组
空间O(1)只用两个变量

两种方法对比

维度方法一 标准 DP方法二 滚动优化
代码复杂度简单中等
时间复杂度O(n)O(n)
空间复杂度O(n)O(1)
面试推荐度容易理解展示优化能力

面试追问 FAQ

问题解答
Q1:为什么状态转移是dp[i] = max(dp[i-2] + nums[i], dp[i-1])因为不能偷相邻的房间。如果选择偷第 i 间房,那么第 i-1 间房一定不能偷,最大收益是dp[i-2] + nums[i]。如果不偷第 i 间房,最大收益就是dp[i-1]。取两者较大值。
Q2:初始化dp[1] = max(nums[0], nums[1])是为什么?只有两间房时,不能同时偷(相邻),所以只能选金额大的那间。
Q3:如果房屋是环形的怎么办?如果首尾相连(环形),需要分两种情况:1)不偷最后一间房,2)不偷第一间房。取两种情况的最大值。时间复杂度仍是 O(n)。
Q4:空间优化背后的原理是什么?状态转移只用到dp[i-1]dp[i-2],更早的历史状态不再需要。所以只需要两个变量滚动存储即可。
Q5:如何理解"打家劫舍"和"爬楼梯"的区别?爬楼梯是dp[i] = dp[i-1] + dp[i-2](累加),打家劫舍是dp[i] = max(dp[i-1], dp[i-2] + nums[i])(选择)。爬楼梯必须走到最后,打家劫舍可以选择偷或不偷。
Q6:如果每间房有多个分支(树形)怎么解?这是"打家劫舍 III"的变体,使用后序遍历 + 树形 DP,思路是:如果偷父节点就不能偷子节点,如果不偷父节点可以选择偷或不偷子节点。

相关题目

题目难度关键点
198. 打家劫舍中等一维 DP,房间选择
213. 打家劫舍 II中等环形房屋
337. 打家劫舍 III中等树形 DP
70. 爬楼梯简单斐波那契数列
746. 使用最小花费爬楼梯简单加权选择

总结

要点说明
核心原理dp[i] = max(dp[i-1], dp[i-2] + nums[i])
初始化dp[0] = nums[0]dp[1] = max(nums[0], nums[1])
时间复杂度O(n)
空间复杂度O(n),可优化到 O(1)

http://www.jsqmd.com/news/814245/

相关文章:

  • EHDB280频谱驱动接触器
  • 终极指南:5分钟用MediaCreationTool.bat绕过TPM限制安装Windows 11
  • 突破性开源甘特图工具:GanttProject专业级项目管理实战指南
  • 工业自动化系统架构与通信协议技术解析
  • Spring AI结合Ollama(三)
  • 构建AI模型API桥接器:实现OpenAI格式与私有模型服务的无缝对接
  • 从校园到职场:技术新人必须完成的3个思维转变
  • 容器化应用部署实战:从拉取未知镜像到生产级运维全解析
  • 八大网盘直链解析终极指南:告别限速,实现全速下载
  • 2026年注册分公司费用排名,哪家服务区域广 - mypinpai
  • Animo:用AI将代码对话实时转为动画视频的编辑器扩展
  • 【Bug故事】那些难忘的调试经历与方法论
  • 8088单板机DIY--串口转换(一)
  • GPT宏系统开发指南:从提示词模板到RAG知识库的自动化实践
  • 层序遍历:BFS核心技巧
  • 2026年分公司注册靠谱排名 - mypinpai
  • 2026年3月市场可靠的除尘器企业推荐,蘑菇菌渣制粒机/木材粉碎机/精饲料制粒机/燃料搅拌机/菌渣烘干机,除尘器公司推荐 - 品牌推荐师
  • 开源项目贡献流程标准化:CLA与Issue/PR模板实践指南
  • AI应用安全新挑战:基于模糊测试的提示词注入漏洞自动化检测
  • 2026年技术过硬的深圳小程序制作推荐榜单
  • DevSquad:AI多智能体协同开发平台架构与实战指南
  • 3分钟快速上手:Figma中文界面插件的终极解决方案
  • 全栈AI聊天应用LLMChat:FastAPI+Flutter构建与本地部署实战
  • Python自动化脚本:模拟鼠标键盘输入保持系统活跃状态
  • macOS开发者必备:Cacheout智能缓存清理工具详解与实战
  • 可灵活扩展的企业即时通讯工具对比分析:从三个维度看清选型本质 - 小天互连即时通讯
  • 大语言模型剪枝技术:Týr-the-Pruner框架解析
  • 从协同过滤到深度学习:Spark机器学习实战三部曲
  • RISC-V开源处理器IP:模块化设计与低功耗嵌入式应用实践
  • 面向对象的架构