开篇

动态规划的问题我们已经说过很多了,今天这篇博客将是最后一篇面试题详解中的动态规划算法问题,接下来我们会对其他算法进行一些类型题的更新——如回溯法,贪心法。以及一些数据结构的典型例题,如hash,链表,栈,二叉搜索树(这个是最重要的),堆(优先队列)等等进行基础问题的更新。(主要借鉴labuladong的文章及顺序)
今天我们要说的以是一道leetcode上的一种类型题——打家劫舍。我在大二的时候,基本毫无算法基础,仅仅会python和C++编程,就受到了某些名人的怂恿,大胆地去面试了,现在想想当初自己真的自信,反而现在有了算法功底以后开始惧怕笔试题了,可能这就是传说中的无知者无畏吧哈哈哈哈。当初面试的时候面试官让我手撕代码,就是打家劫舍问题,当时根本没有leetcode的概念,计算机基本0基础,想靠着自己仅有的C++的基本语句蒙混过关,然而结局可以预知的惨烈,但是打家劫舍问题其实就是一种很简单的动态规划问题,代码量少,思路简单,这篇文章就带你团灭掉所有打家劫舍类型题。

打家劫舍问题

打家劫舍问题一


我们发现我们不能偷相邻的两个住户,因此如果你偷了1号,2号就不能再偷了,但是你可以偷三号…找出最大的偷窃金额即可。
典型的动态规划问题,我们还是按照老方法——找状态,穷举,做选择
我们先考虑一下状态是什么,状态就是住户的序号和现有金额,而抢或者不抢就是我们的选择。

在两个选择种,每次都选更大的结果,最后得到的就是最多能抢到的钱:

int rob(int nums[])
{return dp(nums,0);
}
//返回nums[start....]能抢到的最大值
int dp(int nums[],int start)
{if(start >= nums.length())return 0;int res = max(//不抢,去下家dp(nums,start+1),//抢,去下下家dp(nums[start]+dp[nums,start+2]);)return res;
}

从代码中我们其实就已经明确了状态转移,就可以发现对于同一个start位置,是存在重叠子问题的,比如下图:

上述代码我们每次都会进入一个递归之中,但是这样岂不是很浪费时间?所以说存在重叠子问题,可以用备忘录进行优化,在上述的代码中进行优化,在每次求解之前现在备忘录中查找,如果查找得到就直接返回,否则就计算结果:max(nums[start] + dp(nums,start+2),dp(nums,start+1))
上述都是一些自顶向下的方法,但我们也可以采用自底向上的方法,从n-1遍历到0.
我们又发现,状态转移之和dp[i]最近的两个状态有关,所以可以进一步优化,将空间复杂度降低为O(1)

int rob(int nums[])
{int n = nums.length();//记录dp[i+1]和dp[i+2]int dp_i_1 = 0,dp_i_2 = 0;//记录dp[i]int dp_i = 0;for(int i = n - 1;i >= 0;i--){dp_i = max(dp_i_2 + nums[i],dp_i_1);dp_i_2 = dp_i_1;dp_i_1 = dp_i;}return dp_i;
}   

打家劫舍问题二
这个问题基本和问题一是一样的,只不过房子的排列方式略有不同,问题一里房子排列成了一排,而问题二中所有房子围成了一个圈。所以第一家住户和最后一家住户不能同时被偷。
这里的解决办法其实没什么特别的,和问题一有异曲同工的技巧。只不过在这里,我们限定选择住户的范围。
我们知道,动态规划法最重要的就是状态选择。问题一里我们直接在所有房子中考虑我们偷哪些房子。但是在问题二中,我们需要限定一下考虑的范围,不能在所有房子中进行考虑,而只能在部分房子中考虑。
由于,首尾两个住户不能投同时被偷,我们就限定两个范围,第一个范围包括首,不包括尾;第二个范围包括尾不包括首。但其实还有一种范围,就是二者都不包括,但是这种范围一定比前两者偷到的钱数少,所以我们不做考虑。

int rob(int nums[])
{int n = sizeof(nums) / sizeof(nums[0]);if(n==1)return nums[0];return max(robrange(nums,0,n-2),robrange(nums,1,n-1));
}
//仅计算闭区间[start,end]的最优结果
int robrange(int nums[],int start,int end)
{int n = sizeof(nums) / sizeof(nums[0]);int dp_i_1 = 0,dp_i_2 = 0;int dp_i = 0;for(int i = end;i >= start;i--){dp_i = max(dp_i_1,nums[i]+dp_i_2);dp_i_2 = dp_i_1;dp_i_1 = dp_i;}return dp_i;

打家劫舍问题三
问题三也是改变了所有住户的住房结构,不再是一排或者一圈了,而是一棵二叉树。房子在二叉树的节点上,相连的两个房子不能同时被抢劫。

我们的整体思路没有变,仍然是在枪和不抢中去做选择,我们可以直接套用代码

int rob(TreeNode root)
{int res[] = dp(root);return max(res[0],res[1]);
}//返回一个大小为2的数组arr
//arr[0]表示不抢root的话可以得到的最大钱数
//arr[1]表示抢root的话可以得到的最大钱数
vector<int>dp(TreeNode root)
{if(!root)return [0,0];//其中left[0]表示左边不抢,left[1]表示左边抢,right同理vector<int>left = dp(root.left);vector<int>right = dp(root.right);//抢,下家就不能抢了int rob = root.val+left[0]+right[0];//不抢,下家可以抢也可以不抢,取决于收益int not_rob = max(left[0],left[1])+max(right[0],right[1]);return [rob,not_rob];    

时间复杂度为O(N),空间复杂度只有递归函数堆栈所需的空间,不需要备忘录的额外空间
大家也可以选择备忘录的方式,这里说一下大致的思路和关键的状态转移方程,不写代码了:
首先创建一个hashtable来存储访问每个结点对应的最大钱数,这样下来可以节省一些时间,状态转移方程也是采用递归的方式:

int rob(TreeNode root)//抢根节点int do_it = root.val + (root.left == null ? 0 : rob(root.left.left) + rob(root.left.right)) +(root.right==null ? 0 : rob(root.right.right)+rob(root.right.left));//不抢根节点int not_do_it = rob(root.left) + rob(root.right);int ans = max(do_it,not_do_it);
}       

总结

这是动态规划的最后一篇文章了,在这里最后对动态规划做一下总结。
动态规划其实最重要的框架再说下去就吐了,状态,选择,状态转移方程
如果我们能写出状态转移方程和base case,那么问题基本就解决了90%了,但是状态转移方程起码要有状态吧,所以确定状态也是很重要的。所以大家在读题的时候一定要找出题目中的所有状态(与答案密切相关的可变量),然后对状态穷举,专注于写出状态转移方程,然后写出base case,基本就八九不离十了。
有的朋友一定会说,这个状态转移方程那哪有那么容易写啊。是的,我到现在都有这样的感觉,但是当你刷题多了,会发现状态转移方程就那么几种,见得多了自然能想到了。在这一系列面试题详解的动态规划和我的另一系列动态规划专栏的文章中,基本介绍了所有动态规划的典型问题,状态转移方程无非这几种,大家可以多看多思考,学会状态转移方程的书写,基本可以秒杀动态规划问题。

团灭Leetcode打家劫舍问题相关推荐

  1. 终于等到你,Alibaba首发:大师级算法宝典,足以团灭LeetCode

    前言: 说到算法,相信每一个程序员和接触过程序员的朋友都不会陌生,直到现在算法一直占着面试必问的地位,而算法面试也仍是当前最适合公司筛选程序员的方法之一,在阿里,字节跳动.华为等公司带动下,无论是求职 ...

  2. C刷题:一个方法团灭LeetCode股票买卖问题

    C刷题:一个方法团灭LeetCode股票买卖问题 核心框架 5道题中的易错点 买卖股票的最佳时机 买卖股票的最佳时机 II 买卖股票的最佳时机 III 最佳买卖股票时机含冷冻期(中等) 买卖股票的最佳 ...

  3. 回溯算法团灭子集、排列、组合问题

    回溯算法团灭子集.排列.组合问题 一.子集 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = [1,2,3] ...

  4. LeetCode 打家劫舍问题

    LeetCode 打家劫舍问题 一:House Robber1 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间 ...

  5. kido机器人没反应_机器人不能钩的三个英雄,章鱼妈上榜,钩中图四我方直接团灭!...

    话不多说,点赞关注走一波,小伙伴们好我是小贤,欢迎来到<小贤联盟笔记>第十二期,上一期我们讲到了最丢人的几种死法(关注查看历史内容),那么今天我们来说说机器人不能钩的三个英雄,章鱼妈上榜, ...

  6. 特斯拉奔驰宝马沃尔沃团灭!安全的半自动驾驶?不存在的

    方栗子 发自 凹非寺  量子位 出品 | 公众号 QbitAI 美国高速公路安全保障协会 (IIHS) ,是以严格闻名的汽车安全评级机构.  所有的汽车,进了IIHS的实验室,便可能经受地狱考验. ...

  7. 计算机科学与技术大学容易挂科吗,大一容易“挂科”的4个学科,学霸都未必敢报,最后一个“团灭”...

    很多人都觉得大学的生活是轻松的,多数时间都是自由安排,虽然事实就是如此,但是大学的生活也并没有大家想像的那么轻松. 大学期间每天一般会有3节课左右,但每节课的课时都会有所增加,由高中时期的每节课45分 ...

  8. 诚之和:字节再迎裁员潮温州百人“大撤退”、ohayoo应届生“团灭”

    "字节裁员"再次登上微博热搜. 近日,字节被曝温州本地直营中心撤城裁员,游戏业务ohayoo包括应届生在内的大量员工也被裁了,实行N+1赔付. 10月19日,字节跳动hr相关负责人 ...

  9. 团灭了3个月的线下营销,还有希望吗?

    如果要说营销哪个部分是受疫情打击最大的,那绝对是线下营销,不仅MWC.日内瓦车展.西南偏南这样的国际顶尖展会全部团灭,包括谷歌开发大会之类的企业秀肌肉活动也一起崩盘,连NBA等一干运动赛事都开始停摆, ...

  10. 威漫哨兵机器人_漫威中实力最强的五大机器人,哨兵机器人能够团灭变种人!...

    漫威中实力最强的五大机器人,哨兵机器人能够团灭变种人! 众所周知,漫威宇宙的科技非常的先进,已经超出了人类的认知,在漫威世界中,有着各种各样的黑科技,比如钢铁侠的钢铁战甲.时光机.纳米技术等等,今天我 ...

最新文章

  1. Linux中iptraf命令详解(IP局域网监控工具)
  2. MySQL分库分表环境下全局ID生成方案
  3. .Net 4.0并行库实用性演练
  4. hadoop 替代方案_如何通过比较替代方案做出有效的决定
  5. 苏宁易购:公司改选董事 同意聘任张近东为公司名誉董事长
  6. C#中使用GDI+实现复杂打印
  7. Gentoo安装MarkDown编辑器Haroopad
  8. 产品读书《用户故事与敏捷方法》
  9. VScode Remote SSH连接失败
  10. weex的组件 web的使用(结合webview模块)
  11. Linux就业技术指导:简历项目经验示例
  12. 双三次差值bicubic
  13. 【转载】Web前端框架图
  14. 手机app开发成本预算
  15. Python dataframe绘制饼图_Python可视化29|matplotlib-饼图(pie)
  16. 农夫山泉2面面试经历
  17. linux locale 编译,Linux locale 缺失和安装
  18. 测试开发(社招)面经:度小满
  19. html页面打印插件,分享8款网站开发中最好用的打印页面插件
  20. 机电毕业设计----利用CC2530芯片开发的基于ZigBee技术的灌溉模拟系统----LED显示屏代码解释(源代码)

热门文章

  1. 哥德巴赫猜想C++实现
  2. 链接mysql 504_常见错误类型502与504
  3. 【记录】codeReview总结
  4. catboost原理
  5. 《东周列国志》第十三回 鲁桓公夫妇如齐 郑子亹君臣为戮
  6. CodeWisdom软件供应链系列学术报告:第2期
  7. 谈谈毕业之后的第一份工作的感悟
  8. Shadowing Japanese Unit 5
  9. AntD Pro v5记录-布局
  10. _itemmod_enchant_groups