(Java)leetcode-337 House Robber III(打家劫舍III)

2023-10-29

题目描述

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:

输入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:

输入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

思路1:递归(自上而下)

先找递归关系——
对于任意一个节点(房屋),有两种选择:
(1)抢当前节点 + 孙子们(下家的下家,最多能抢四家)
(2)要么放弃当前节点,抢儿子们(下家,最多两家)

所以问题归结为计算哪种方案得到的东西更多。

将以上描述写成状态转移式,就是本题的关键:

rob(root) = Math.max( rob(root.grandson) +root.val, rob(root.son) )

根据上式,可以写出暴力的自上而下的递归解法:

// 暴力解
// 要么抢当前root+孙子,要么放弃当前,抢儿子
class Solution {
    public int rob(TreeNode root) {
    	if(root == null) return 0;
    	int son = 0;
    	int grandson = 0;
    	if(root.left != null) {
    		son += rob(root.left);
    		grandson += rob(root.left.left) + rob(root.left.right);  		 
    	}
    	if(root.right != null) {
    		son += rob(root.right);
    		grandson += rob(root.right.left) + rob(root.right.right);;   		
    	}
    	return Math.max(grandson + root.val, son);
    }
}

执行用时:
585 ms, 在所有 Java 提交中击败了33.80%的用户
内存消耗:39.3 MB, 在所有 Java 提交中击败了33.33%的用户

思路2:带备忘录的递归

上面的方法由于进行了许多重复计算,因此耗时当然巨大。
我们可以用备忘录(HashMap)来存储已经计算过的节点和对应的结果,需要计算时先看备忘录中是否已经有记录,省去了重复计算,时间复杂度直接降到O(N)。

代码

// 备忘录
class Solution {
	HashMap<TreeNode, Integer> map = new HashMap<>();
    public int rob(TreeNode root) {
    	if(root == null) return 0;
    	if (map.containsKey(root)) return map.get(root);
    	int son = 0;
    	int grandson = 0;
    	if(root.left != null) {
    		son += rob(root.left);
    		grandson += rob(root.left.left) + rob(root.left.right);  
    	}
    	if(root.right != null) {
			son += rob(root.right);
    		grandson += rob(root.right.left) + rob(root.right.right);;  
    	}
    	int res = Math.max(grandson + root.val, son);
    	map.put(root,res);
    	return res;
    }
}

执行用时:3 ms, 在所有 Java 提交中击败了59.14%的用户
内存消耗:39.6 MB, 在所有 Java 提交中击败了33.33%的用户

思路3:自下而上(无备忘录)

这道题让巧妙的点在于, 还有更漂亮的解法——
重新定义了一个节点的两个状态:

(1) 抢,那么下家不能抢
(2) 不抢,那么下家抢不抢,取决于收益大小

递归函数需要返回的是当前节点的两种状态。
子结点的状态陆续汇报信息给父结点,一层一层向上汇报,最后在根结点汇总值。因此采用后序遍历

class Solution {	
	int rob(TreeNode root) {
		int[] res = dp(root);
		return Math.max(res[0], res[1]);
	} 

	/* 返回⼀个⼤⼩为 2 的数组 arr
	arr[0] 表⽰不抢 root 的话, 得到的最⼤钱数
	arr[1] 表⽰抢 root 的话, 得到的最⼤钱数 */

	int[] dp(TreeNode root) {
		if (root == null)
		return new int[]{0, 0};
		int[] left = dp(root.left);
		int[] right = dp(root.right);
		// 抢, 下家就不能抢了
		int rob = root.val + left[0] + right[0];
		// 不抢, 下家可抢可不抢, 取决于收益⼤⼩
		int not_rob = Math.max(left[0], left[1])+ Math.max(right[0], right[1]);
		
		return new int[]{not_rob, rob};
		}
}

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:39.7 MB, 在所有 Java 提交中击败了33.33%的用户

这种解法和上面的思路不⼀样, 修改了递归函数的定义, 略微修改了思路, 使得逻辑⾃洽, 依然得到了正确的答案, ⽽且代码更漂亮。

这也体现了DP问题的一个特性:「不同定义产⽣不同解法」 。

实际上, 这个解法⽐前面的两种解法运⾏时间要快得多, 虽然算法分析层⾯时间复杂度是相同的。 原因在于此解法没有使⽤额外的备忘录, 减少了数据操作的复杂性, 所以实际运⾏效率会快。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(Java)leetcode-337 House Robber III(打家劫舍III) 的相关文章

  • Java中ArrayList的交集和并集

    有什么方法可以做到这一点吗 我正在寻找 但没有找到 另一个问题 我需要这些方法 以便我可以过滤文件 有些是AND过滤器 有些是OR过滤器 就像集合论中的那样 所以我需要根据所有文件和保存这些文件的联合 相交 ArrayList 进行过滤 我
  • Gradle 构建错误:无法从 https://repo1.maven.org/maven2/io/fabric/tools/gradle/maven-metadata.xml 加载 Maven 元数据

    我在 Android studio 中遇到 gradle 构建错误 如下所示 Error A problem occurred configuring project MyApp Could not resolve all dependen
  • 如何通过 javaconfig 使用 SchedulerFactoryBean.schedulerContextAsMap

    我使用 Spring 4 0 并将项目从 xml 移至 java config 除了访问 Service scheduleService 带注释的类来自QuartzJobBean executeInternal 我必须让它工作的 xml 位
  • Java 枚举与创建位掩码和检查权限的混淆

    我想将此 c 权限模块移植到 java 但是当我无法将数值保存在数据库中然后将其转换为枚举表示形式时 我很困惑如何执行此操作 在 C 中 我创建一个如下所示的枚举 public enum ArticlePermission CanRead
  • 如何循环遍历所有组合,例如48 选择 5 [重复]

    这个问题在这里已经有答案了 可能的重复 如何在java中从大小为n的集合中迭代生成k个元素子集 https stackoverflow com questions 4504974 how to iteratively generate k
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • Java 公历日历更改时区

    我正在尝试设置 HOUR OF DAY 字段并更改 GregorianCalendar 日期对象的时区 GregorianCalendar date new GregorianCalendar TimeZone getTimeZone GM
  • 检测并缩短字符串中的所有网址

    假设我有一条字符串消息 您应该将 file zip 上传到http google com extremelylonglink zip http google com extremelylonglink zip not https stack
  • java.lang.IllegalStateException:提交响应后无法调用 sendRedirect()

    这两天我一直在尝试找出问题所在 我在这里读到我应该在代码中添加一个返回 我做到了 但我仍然得到 java lang IllegalStateException Cannot call sendRedirect after the respo
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 如何对不同的参数类型使用相同的java方法?

    我的问题 我有 2 个已定义的记录 创建对象请求 更新对象请求 必须通过实用方法进行验证 由于这两个对象具有相同的字段 因此可以对这两种类型应用相同的验证方法 现在我只是使用两种方法进行重载 但它很冗长 public record Crea
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • 专门针对 JSP 的测试驱动开发

    在理解 TDD 到底是什么之前 我就已经开始编写测试驱动的代码了 在没有实现的情况下调用函数和类可以帮助我以更快 更有效的方式理解和构建我的应用程序 所以我非常习惯编写代码 gt 编译它 gt 看到它失败 gt 通过构建其实现来修复它的过程
  • Eclipse 启动时崩溃;退出代码=13

    I am trying to work with Eclipse Helios on my x64 machine Im pretty sure now that this problem could occur with any ecli
  • 在java中为组合框分配键

    我想添加一个JComboBox在 Swing 中这很简单 但我想为组合中的每个项目分配值 我有以下代码 JComboBox jc1 new JComboBox jc1 addItem a jc1 addItem b jc1 addItem

随机推荐

  • 计算机重新如何连接网络打印机,电脑怎样连接打印机,小编教你电脑如何连接网络打印机...

    打印机是办公室里经常会用到的一种办公设备 由于工作性质的不同 以及其他原因 网络打印机可以实现多台电脑连接 实现资源共享 网络打印机自带ip 只需指定ip就可以快速连接 那电脑如何连接网络打印机 下面 小编给大家讲解电脑连接网络打印机的技巧
  • 基于类帕累托贯序抽样算法求解单目标优化问题附matlab代码

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab完整代码及仿真定制内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器
  • 智能合约平台开发指南

    随着区块链技术的普及 智能合约平台已经成为了这个领域的一个重要趋势 智能合约可以自动化执行合同条款 大大减少了执行和监督合同条款所需的成本和时间 那么 如何开发一个智能合约平台呢 以下是一些关键步骤 一 选择合适的区块链平台 智能合约通常运
  • pgsql数据库实现导入导出

    pgsql数据库实现导入导出 1 导出表 pg dump h 数据库ip U 用户名 数据库名 t 表名 gt 路径 例 pg dump h 127 0 0 1 U sysdba data center t book gt data boo
  • Prompt入门

    Prompt的范式大抵是两种 续写Prefix 用在GPT2 3那种单向LM预训练模型上 输入 好好学习 翻译成英文 输出 good good study 完形填空 用在BERT那种MLM式预训练模型上 比如情感分类任务可以输入 这个饼不错
  • Idea 中 Git 不提交当前分支修改代码并切换分支

    1 当前分支修改代码切换分支 日常开发中 我们可能会碰到我们正在修改当前 01 分支的代码 突然要去修改另外一个 02 分支的代码情况 而我们 01 分支写的代码还未经过测试 并不能马上提交 这个时候我们切换到 02 分支就会有问题 比如弹
  • dubbo中的Mock实现机制

    Mock是SOA之中的一个很有用的功能 不仅可以用来进行服务降级 也可以用来在测试中模拟服务调用的各种异常情况 dubbo框架里面的mock是在服务使用者这一端实现的 下面对实现机制进行分析 1 Mock的植入 很显然 既然提供了mock机
  • C++spdlog学习总结

    C Spdlog学习笔记 spdlog简介 spdlog优点 一般日志功能设计 spdlog安装 spdlog琐碎知识点总结 spdlog程序测试 一 日志输出控制台 1 数据全部输出到控制台 2 指定某个等级以上的数据到控制台 二 输出格
  • OC门与OD门

    OC门与OD门 OC Open Collector 集电极开路 OD Open Drain 漏极输出 集电极开路输出的结构如下图所示 右边的那个三极管集电极什么都不接 所以叫做集电极开路 左边的三极管为反相使用 使输入为 0 时 输出也为
  • 底层进阶

    在知乎上关注了好多图形学大佬 感觉现在知乎的技术氛围要比掘金推荐旧文好多了 经常会推送感兴趣的领域内容 而且还可以和作者私信交流 这段时间看到有大佬分享 GPU 架构相关的内容 做图像渲染的还是要懂 GPU 才行的 毕竟是和它打交道嘛 这位
  • springboot @CreatedDate @LastModifiedDate 自动生成创建时间,修改时间

    CreatedDate或 LastModifiedDate 在实体类的属性上加上上面的注解 即可不用处理时间的问题 在插入时会自动生成创建时间 修改时自动更新修改时间 搭配 Column updatable false 使用 注解起作用还需
  • C++ 随机数的制作

    include
  • Spring security安全登录-当AJAX遇上Redirect

    前言 最近做平台引入spring security做为安全认证框架 在登录的时候使用的ajax的请求方式 最开始的做法是调用登录的接口 成功后再前端使用window location href index html的方式跳转到希望的页面 考
  • kali linux 2020.4 自带浏览器英文改中文

    刚开始用kali linux 可能一些伙伴也会像我一样遇到英文不通的情况 比如 系统自带的火狐浏览器是英文的 要变成中文直接敲入 sudo apt install firefox esr l10n zh cn y 然后重启一下火狐浏览器就可
  • 【Unity+MySQL】实现简单的注册登录系统

    目录 1 安装Unity引擎和Navicat软件 2 安装MySQL8 0数据库 2 1 下载msi文件 2 2 安装MySQL Server 8 0 2 3 配置环境变量 2 4 安装MySQL服务 2 5 开启MySQL服务 2 6 修
  • 软件外包开发项目管理工具

    随着软件项目的规模越做越大 项目管理人员需要使用工具管理项目进度 从而更有成效的管理好软件开发进度 软件开发的进度管理工具有很多 今天和大家分享一些常用的系统工具 希望对大家有所帮助 北京木奇移动技术有限公司 专业的软件外包开发公司 欢迎交
  • Hololens 学习-----1

    ww 学习资料 基本操作 链接 https learn microsoft com zh cn hololens hololens2 basic usage 链接 https learn microsoft com zh cn window
  • Java中Collection集合和Map集合详解(进阶三)

    目录 友情提醒 第一部分 单列集合 第一章 单列集合体系 Collection接口 1 1 单列集合是什么 与数组的区别在哪 1 2 单列集合体系与分类 第二章 单例集合体系Collection下的List接口 Set接口 2 0 List
  • JAVA随机生成十个不重复的整数(Arraylist,Set)

    随机生成十个不重复的整数有许多方法 这里我只写出两种 第一种 Set 先上代码 import java util HashSet import java util Set public class Demo01 public static
  • (Java)leetcode-337 House Robber III(打家劫舍III)

    题目描述 在上次打劫完一条街道之后和一圈房屋后 小偷又发现了一个新的可行窃的地区 这个地区只有一个入口 我们称之为 根 除了 根 之外 每栋房子有且只有一个 父 房子与之相连 一番侦察之后 聪明的小偷意识到 这个地方的所有房屋的排列类似于一