LeetCode-337题:打家劫舍III(原创)

【题目描述】

        小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列

输入: root = [3,2,3,null,3,null,1]
输出: 7 
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

【解题代码】

package dp;import common.Utils;
import tree.binarytree.TreeNode;import java.util.*;public class Rob3 {// 中间结果缓存mapprivate Map<Integer, Integer> intResultCache = new HashMap<>();public static void main(String[] args) {//Integer[] array = new Integer[]{3, 2, 3, null, 3, null, 1};//Integer[] array = new Integer[]{3, 4, 5, 1, 3, null, 1};//Integer[] array = new Integer[]{4, 1, null, 2, null, 3};//Integer[] array = new Integer[]{2, 1, 3, null, 4};Integer[] array = Utils.readIntegersFromFile("res\\337\\122.txt");TreeNode root = TreeNode.constructTree(array);long start = System.currentTimeMillis();System.out.println("开始计算。。。");int result = new Rob3().rob(root);System.out.println("运行时长:" + (System.currentTimeMillis() - start) + "ms");System.out.println("The result is: " + result);}public int rob(TreeNode root) {// 从根节点开始进行计算,根节点没有父节点,所以是否抢劫为falsereturn rob(root, false);}private int rob(TreeNode node, boolean parent) {// 节点为空,返回0if (node == null) {return 0;}// 获取当前节点hashcode,如果父节点被抢劫,hashcode加1int hashCode = node.hashCode() + (parent == true ? 1 : 0);int result = intResultCache.getOrDefault(hashCode, 0);// 如果缓存不包含当前节点if (!intResultCache.containsKey(hashCode)) {// 如果当前节点父节点被抢劫,那么当前节点不能被抢劫,只能计算两个子节点之和if (parent) {result = rob(node.left, false) + rob(node.right, false);} else { // 如果当前节点父节点不被抢劫,那么当前节点可以选择抢劫或不抢劫// 当前节点被抢劫, 当前节点抢劫最大金额等于本节点现金加上两个子节点之和int result1 = node.val + rob(node.left, true) + rob(node.right, true);// 当前节点不被抢劫, 当前节点抢劫最大金额等于两个子节点之和int result2 = rob(node.left, false) + rob(node.right, false);// 取两种情况最大值result = Math.max(result1, result2);}// 将计算结果缓存起来intResultCache.put(hashCode, result);}return result;}
}

【解题思路】

        这一道题又是 LeetCode198题LeetCode-198题:打家劫舍(原创)-CSDN博客和 LeetCode213题LeetCode-213题:打家劫舍II(原创)-CSDN博客的变种,和之前的题目很类似,但数据结构从数组变成了树节点,总结出以下几个要点:

  1.  如果当前节点父节点被抢劫,那么当前节点不能被抢劫,当前节点抢劫的最大金额等于两个抢劫子节点的最大金额之和;
  2. 如果当前节点父节点不被抢劫,那么当前节点可以选择抢劫或不抢劫:
  3. 如果选择不抢劫当前节点,当前节点抢劫的最大金额等于两个抢劫子节点的最大金额之和;
  4. 如果选择抢劫当前节点,当前节点抢劫的最大金额等于当前节点金额加上两个抢劫子节点的最大金额之和;
  5. 返回3,4点最大值即可
  6. 数据结构从数组变成了树节点,选择使用递归来实现此算法

按照上述思路很快写出如下代码:

class Solution {public int rob(TreeNode root) {// 从根节点开始进行计算,根节点没有父节点,所以是否抢劫为falsereturn rob(root, false);}private int rob(TreeNode node, boolean parent) {// 节点为空,返回0if (node == null) return 0;if (parent) {// 如果当前节点父节点被抢劫,那么当前节点不能被抢劫,只能计算两个子节点之和return rob(node.left,false) + rob(node.right,false);} else { // 如果当前节点父节点不被抢劫,那么当前节点可以选择抢劫或不抢劫// 当前节点被抢劫, 当前节点抢劫最大金额等于本节点现金加上两个子节点之和int result1 = node.val + rob(node.left,true) + rob(node.right,true);// 当前节点不被抢劫, 当前节点抢劫最大金额等于两个子节点之和    int result2 = rob(node.left,false) + rob(node.right,false);// 取两种情况最大值return Math.max(result1, result2);}}
}

但LeetCode提交之后,报错超时时间限制

查看代码实现,并仔细考虑,觉得问题应该还是出在递归函数存在大量重复调用的情况,如是考虑设计一个缓存来存储已经计算好的节点,按照以下修改代码,再次提交顺利通过

【解题步骤】

  1. 给Solution类定义一个中间结果值缓存的map变量
    private Map<Integer, Integer> intResultCache = new HashMap<>();
  2. 定义一个抢劫节点函数,参数为当前节点,以及父节点是否被抢劫
    private int rob(TreeNode node, boolean parent) {
  3. 获取当前节点hashcode,如果父节点被抢劫,hashcode加1,并从缓存中读取此节点最大金额值

    int hashCode = node.hashCode() + (parent == true ? 1 : 0);
    int result = intResultCache.getOrDefault(hashCode, 0);
  4. 如果缓存不包含当前节点,计算该节点的抢劫金额最大值,并将结果缓存起来

    // 如果缓存不包含当前节点
    if (!intResultCache.containsKey(hashCode)) {// 如果当前节点父节点被抢劫,那么当前节点不能被抢劫,只能计算两个子节点之和if (parent) {result = rob(node.left, false) + rob(node.right, false);} else { // 如果当前节点父节点不被抢劫,那么当前节点可以选择抢劫或不抢劫// 当前节点被抢劫, 当前节点抢劫最大金额等于本节点现金加上两个子节点之和int result1 = node.val + rob(node.left, true) + rob(node.right, true);// 当前节点不被抢劫, 当前节点抢劫最大金额等于两个子节点之和int result2 = rob(node.left, false) + rob(node.right, false);// 取两种情况最大值result = Math.max(result1, result2);}// 将计算结果缓存起来intResultCache.put(hashCode, result);
    }
  5. 最后返回结果result

【思考总结】

  1. 对于树节点的动态规划算法,一般只能使用递归的方式进行计算;
  2. 递归函数存在大量重复调用的情况,可以考虑设计一个缓存来存储已经计算好的节点,这样能大大提高算法性能;
  3. LeetCode解题之前,一定不要看题解,看了就“破功”了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/536603.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue上传文件夹+上传文件vue-simple-uploader

vue上传文件夹上传文件vue-simple-uploader 使用插件 在main.js引入 import uploader from vue-simple-uploaderVue.use(uploader);<el-dialog title"上传文件" :visible.sync"dialogFileVisible" width"50%" :before-close"handleFil…

从零开始学习深度学习库-1:前馈网络

你好&#xff01;欢迎来到这个系列的第一篇文章&#xff0c;我们将尝试用Python构建自己的深度学习库。在这篇文章中&#xff0c;我们将开始编写一个简单的前馈神经网络。我们将仅在这篇文章中处理前向传播&#xff0c;并在下一篇文章中处理网络的训练。这篇文章将介绍基本的前…

学习网络安全:记一次某网站渗透测试过程

本文作者&#xff1a; 汇智知了堂信安教学老师——辉哥 一、信息收集 网站界面 网站信息收集 &#xff08;1&#xff09;中间件信息 &#xff08;2&#xff09;目录扫描 思路&#xff1a;由于是cms的站&#xff0c;针对这种情况&#xff0c;我们可以收集cms的默认目录结构来…

java内部类的作用与优缺点

一、前言 很久没看到java内部类了&#xff0c;今天在审查代码时候&#xff0c;发现了java内部类&#xff0c;主要是内部类还嵌套了内部类。于是记录一下 二、java内部类的作用与优缺点 Java内部类&#xff0c;也称为嵌套类&#xff0c;是定义在另一个类&#xff08;外部类&am…

1.1计算机系统构成及硬件系统知识(上)

基础知识部分----chap01 主要议题&#xff1a; 数制转换&#xff1a;一般会涉及存取的计算&#xff1b;ip地址中变长子网掩码的计算题&#xff1b;&#xff08;难度较大&#xff09; 数的表示&#xff1a;二进制、十六进制&#xff1b; 计算机的组成&#xff1a;考察的较为深入…

【Java语言】遍历List元素时删除集合中的元素

目录 前言 实现方式 1.普通实现 1.1 使用【for循环】 方式 1.2 使用【迭代器】方式 2.jdk1.8新增功能实现 2.1 使用【lambda表达式】方式 2.2 使用【stream流】方式 注意事项 1. 使用【for循环】 方式 2. 不能使用增强for遍历修改元素 总结 前言 分享几种从List中移…

FreeRTOS操作系统学习——任务通知

任务通知介绍 所谓任务通知&#xff0c;也可以反过来通知任务。在以往使用队列&#xff0c;信号量&#xff0c;事件组等等方法时&#xff0c;我们并不知道对方是谁&#xff0c;而在使用任务通知时&#xff0c;可以明确指定通知哪个任务。使用任务通知时&#xff0c;任务结构体…

程序员的三重境界:码农,高级码农、程序员!

见字如面&#xff0c;我是军哥&#xff01; 掐指一算&#xff0c;我在 IT 行业摸爬滚打 19 年了&#xff0c;见过的程序员至少大好几千&#xff0c;然后真正能称上程序员不到 10% &#xff0c;绝大部分都是高级码农而已。 今天和你聊聊程序员的三个境界的差异&#xff0c;文章不…

未来已来:科技驱动的教育变革

我们的基础教育数百年来一成不变。学生们齐聚在一个物理空间&#xff0c;听老师现场授课。每节课时长和节奏几乎一致&#xff0c;严格按照课表进行。老师就像“讲台上的圣人”。这种模式千篇一律&#xff0c;并不适用于所有人。学生遇到不懂的问题&#xff0c;只能自己摸索或者…

【数据结构】详解时间复杂度和空间复杂度的计算

一、时间复杂度&#xff08;执行的次数&#xff09; 1.1时间复杂度的概念 1.2时间复杂度的表示方法 1.3算法复杂度的几种情况 1.4简单时间复杂度的计算 例一 例二 例三 1.5复杂时间复杂度的计算 例一&#xff1a;未优化冒泡排序时间复杂度 例二&#xff1a;经过优化…

JAVA初阶数据结构链表(2)双向链表( +专栏数据结构练习是完整版)

1.双向链表的结构&#xff08;双向不带头不循环链表&#xff09; 需要注意的一点就是&#xff0c;在jdk中的链表就是双向链表 一个节点有三个域 val&#xff08;数值域&#xff09; next&#xff08;地址域&#xff09; prev&#xff08;前驱记录前一个节点的地址&#xff09…

Express学习(四)

使用Express写接口 创建基本的服务器 创建API路由模块 编写GET接口 编写POST接口 CORS跨域资源共享 什么是CORS CORS由一系列HTTP响应头组成&#xff0c;这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源。浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如…