代码随想录算法训练营第四十八天 | 力扣 198.打家劫舍, 213.打家劫舍II, 337.打家劫舍III

news2025/1/23 4:52:38

198.打家劫舍

题目

198. 打家劫舍

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

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

解析

当前房屋偷与不偷取决于 前一个房屋和前两个房屋是否被偷了。

1.确定dp数组(dp table)以及下标的含义

dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]

2.确定递推公式

决定dp[i]的因素就是第i房间偷还是不偷。

如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。

如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点

然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);

3.p数组如何初始化

从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]

从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);

4.确定遍历顺序

dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历!

5.举例推导dp数组

以示例二,输入[2,7,9,3,1]为例。

Java代码实现

public int rob(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    if (nums.length == 1) {
        return nums[0];
    }
    int[] dp = new int[nums.length];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0], nums[1]);
    for (int i = 2; i < nums.length; i++) {
        dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
    }
    return dp[nums.length - 1];
}

213.打家劫舍II

题目

213. 打家劫舍 II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

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

解析

对于一个数组,成环的话主要有如下三种情况:

  • 情况一:考虑不包含首尾元素

  • 情况二:考虑包含首元素,不包含尾元素

  • 情况三:考虑包含尾元素,不包含首元素

注意这里用的是"考虑",例如情况三,虽然是考虑包含尾元素,但不一定要选尾部元素! 对于情况三,取nums[1] 和 nums[3]就是最大的。

而情况二 和 情况三 都包含了情况一了,所以只考虑情况二和情况三就可以了

Java代码实现

public int rob(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    if (nums.length == 1) {
        return nums[0];
    }
    int[] dp = new int[nums.length];
    int result1 = rob(nums, 0, nums.length - 2);
    int result2 = rob(nums, 1, nums.length - 1);
    return Math.max(result2, result1);
}

public int rob(int[] nums, int start, int end) {
    if (start == end) {
        return nums[start];
    }
    int[] dp = new int[nums.length];
    dp[start] = nums[start];
    dp[start + 1] = Math.max(nums[start], nums[start + 1]);
    for (int i = start + 2; i <= end; i++) {
        dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
    }
    return dp[end];
}

337.打家劫舍III

题目

337. 打家劫舍 III

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

解析

对于树的话,首先就要想到遍历方式,前中后序(深度优先搜索)还是层序遍历(广度优先搜索)。

本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算

 如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子(注意这里说的是“考虑”

这道题目算是树形dp的入门题目,因为是在树上进行状态转移,我们在讲解二叉树的时候说过递归三部曲,那么下面我以递归三部曲为框架,其中融合动规五部曲的内容来进行讲解

1.确定递归函数的参数和返回值

这里我们要求一个节点 偷与不偷的两个状态所得到的金钱,那么返回值就是一个长度为2的数组。

参数为当前节点,代码如下:

int[] robTree(TreeNode cur)

其实这里的返回数组就是dp数组。

所以dp数组(dp table)以及下标的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。

所以本题dp数组就是一个长度为2的数组!

2.确定终止条件

在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回

if (cur == NULL) return int[]{0, 0};

这也相当于dp数组的初始化

3.确定遍历顺序

首先明确的是使用后序遍历。 因为要通过递归函数的返回值来做下一步计算。

通过递归左节点,得到左节点偷与不偷的金钱。

通过递归右节点,得到右节点偷与不偷的金钱。

4.确定单层递归的逻辑

偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; (如果对下标含义不理解就再回顾一下dp数组的含义

如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的,所以:val2 = max(left[0], left[1]) + max(right[0], right[1]);

最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}

5.举例推导dp数组

以示例1为例,dp数组状态如下:(注意用后序遍历的方式推导

最后头结点就是 取下标0 和 下标1的最大值就是偷得的最大金钱。 

Java代码实现

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

private int[] robAction(TreeNode root) {
    int[] res = new int[2];
    if (root == null) {
        return res;
    }
    int[] left = robAction(root.left);
    int[] right = robAction(root.right);

    res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
    res[1] = root.val + left[0] + right[0];
    return res;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

word文档生成PDF文档时候自动生成书签方法

0 Preface/Foreword 在日常工作中&#xff0c;经常需要写技术文档&#xff0c;为了排版美观&#xff0c;一般会选择word&#xff0c;这样就可以生成目录。 word文件可以很方便生产PDF文档&#xff0c;方便分享给同事。 在阅读PDF文档时&#xff0c;看到有些PDF文档在左侧有一…

Kafka入门(安装和SpringBoot整合)

文章目录 一、Docker安装Kafka1. 创建网络2. 安装zookeeper3. 安装Kafka 二、Kafka介绍1. Kafka简介 三、SpringBoot整合Kafka1. 引入pom依赖2. application.propertise配置3. Hello Kafka(Producer)4. Consumer Kafka5. 带回调的生产者6. 自定义分区器7. kafka事务提交8. 指定…

如何将 O2OA (翱途) 集成到阿里钉钉

O2OA 平台拥有配套的原生开发的安卓和 IOS 移动 APP&#xff0c;可以以微应用的方式集成到阿里钉钉&#xff0c;同步钉钉的企业通讯录作为本地组织人员架构&#xff0c;并且可以将待办等通知直接推送到钉钉进行消息提醒。本篇主要介绍如何将 O2OA 集成到阿里钉钉实现钉钉办公。…

欧科云链(01499.HK)成格林威治经济论坛钻石级行业独家合作伙伴

5月30日讯&#xff0c;欧科云链控股有限公司&#xff08;“欧科云链控股”&#xff0c;1499.HK&#xff09;官宣成为全球高端经济峰会格林威治经济论坛(Greenwich Economic Forum&#xff0c;下称GEF论坛)的钻石级行业独家合作伙伴。该论坛将于6月15日至16日&#xff0c;首次在…

【维生素C语言】附录:Github 使用教学

&#x1f451; 全新Python高级软件实践专栏&#xff1a; 一起玩蛇啊 &#x1f449; 《一起玩蛇》&#x1f40d; &#x1f4ad; 写在前面&#xff1a;本章我们将介绍 Git 的基本使用方法&#xff0c;包括注册 GitHub 账号、设置 Git、创建本地存储库、复制本地存储库、导入远程…

electron24整合vite4+vue3创建跨端桌面程序

基于Electron集成Vite4.x构建桌面端exe应用 electron24-vite4-vue3 运用最新版本electron结合vite4.x创建vue3桌面端应用程序。 // 版本信息 vite: ^4.3.2 vue: ^3.2.47 electron: ^24.4.0 electron-builder: ^23.6.0创建vitevue3项目 // 初始化项目 npm create vitelatest el…

Echarts绘制K线图,文末源码地址!

文章目录 K线图Apache Echarts绘制K线图完整源码地址 本文中仅展示部分关键代码&#xff0c;文末有完整源码地址&#xff0c;欢迎下载&#xff01; K线图 K线图是一种常见的股票价格走势图表&#xff0c;它是用于显示股票价格变化的一种图表形式。K线图以日、周、月等周期为单…

聊聊分布式解决方案Saga模式

Saga模式 Saga模式使用一系列本地事务来提供事务管理&#xff0c;而一个本地事务对应一个Saga参与者&#xff0c;在Saga流程里面每一个本地事务只操作本地数据库&#xff0c;然后通过消息或事件来触发下一个本地事务&#xff0c;如果其中一个本地事务失败了&#xff0c;Saga就…

一文搞懂激活函数(Sigmoid/ReLU/LeakyReLU/PReLU/ELU)

深度学习算法之前的机器学习算法&#xff0c;并不需要对训练数据作概率统计上的假设&#xff1b;但为了让深度学习算法有更好的性能&#xff0c;需要满足的关键要素之一&#xff0c;就是&#xff1a;网络的输入数据服从特定的分布&#xff1a; 数据分布应该是零均值化的&#…

内核调试环境搭建

内核调试环境搭建 目录 经过测试好用的内核调试环境搭建过程ubuntu和linux版本 查看commit所属的内核版本查看Ubuntu版本号等信息 下载与安装内核 下载内核ubuntu更换内核手动下载并切换到指定源码用apt下载源码使用git下载对应版本 编译并安装linux内核&#xff08;使用linu…

Redis自学之路—分布式锁(四)

目录 分布式锁定义 靠谱的分布式锁具备的特征 【互斥性】 【锁超时释放】 【可重入性】 【高性能和高可用】 【安全性】 Redis分布式锁方案 一、SETNX EXPIRE 二、SETNX value值是&#xff08;系统时间过期时间&#xff09; 三、使用Lua脚本&#xff08;包含SETNXE…

【Web服务器集群】Apache网页优化

文章目录 一、Apache网页优化概述1.优化内容2.网页压缩2.1gzip概述2.2作用2.3Apache的压缩模块概述mod_gzip模块与mod_deflate模块 3.配置网页压缩功能3.1启用网页压缩功能步骤3.2具体操作步骤 4.配置网页缓存功能4.1启用网页压缩功能步骤4.2具体操作步骤 二、Apache安全优化1.…

Unity嵌入AndroidStudio(二) IL2CPP打包

首先建立Unity工程&#xff0c;话不多说直接上图&#xff1a; 导出Android工程&#xff1a; 得到如下文件&#xff0c;备用&#xff1a; 接下来创建安卓项目&#xff1a; 注意包名要和unity里面的一致&#xff0c;sdk版本也要一致 等待编译完成&#xff1a; 打开setting.grade…

2-网络初识——IP地址和端口号

目录 1.IP地址&#xff08;分为IPV4&#xff08;默认情况下&#xff09;和IPV6&#xff09; 1.1.概念 1.2.格式 1.3.特殊IP 2.端口号 2.1.概念 2.2.格式 2.3.注意事项 网络互连的目的是进行网络通信&#xff0c;也即是网络数据传输&#xff0c;更具体一点&#xff0c;是…

SpringMVC源码分析:SpringMVC初始化(一)

一、概述 SpringMVC的初始化大概分为Spring的初始化和SpringMVC的初始化两个部分&#xff0c;他们之间的关系如下图。下面我将按照这个顺序进行详细介绍。 二、Spring初始化 ContextLoaderListener.contextInitialized进行容器的初始化。 继续点进去ContextLoader.initWebApp…

Redis五大数据结构的底层实现(未完成)

一)String类型:可以使用object encoding name就可以查看字符串的编码 SDS&#xff0c;flags的值不同&#xff0c;那么len和alloc所表示的值的数据范围也不同&#xff0c;所以flags的只是为了标识SDS头的总大小&#xff1b; alloc和len刚开始进行申请内存空间的时候都是相同的 S…

简谈你对synchronized关键字的使用

&#x1f468;‍&#x1f393;作者&#xff1a;bug菌 ✏️博客&#xff1a;CSDN、掘金、infoQ、51CTO等 &#x1f389;简介&#xff1a;CSDN|阿里云|华为云|51CTO等社区博客专家&#xff0c;历届博客之星Top30&#xff0c;掘金年度人气作者Top40&#xff0c;51CTO年度博主Top12…

Word控件Spire.Doc 【其他】教程(4):在 Word 中插入上标和下标

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

5.2.1 分类的IP地址

5.2.1 分类的IP地址 通过前面的学习我们知道IPv4协议中包含的内容非常的多&#xff0c;我们学习IPv4又分为几个方面 介绍分类的IP地址IP地址的分配与使用IP分组的格式因特网地址到物理地址的映射&#xff08;ARP协议&#xff09;&#xff0c;用以动态完成IP地址到物理地址映射…

时间序列预测 | Matlab基于灰狼算法优化支持向量机(GWO-SVM)的时间序列预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列预测 | Matlab基于灰狼算法优化支持向量机(GWO-SVM)的时间序列预测 评价指标包括:MAPE、MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %----------------