代码随想录算法训练营第四十八天| LeetCode198. 打家劫舍、LeetCode213. 打家劫舍 II、LeetCode337. 打家劫舍 III

news2025/1/12 20:55:25

一、LeetCode198. 打家劫舍

        1:题目描述(198. 打家劫舍)

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

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

        2:解题思路

class Solution:
    def rob(self, nums: List[int]) -> int:
        # 1:确定dp数组的定义:dp[j]表示下标j(包括j)以内的房屋,最多可以偷窃的金额为dp[j]
        # 2:确定递推公式,分两种,j号房偷和不偷
        # j号房偷,dp[j] = dp[j-2]+nums[j]
        # j号房不偷,dp[j] = dp[j-1]
        # 要取偷窃的最大金额,所以dp[j]=max(dp[j-2]+nums[j], dp[j-1])
        # 3:初始化,根据递推公式,后面的需要由j-2,j-1推出,所以dp[0],dp[1]需要进行初始化
        # dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1])
        # 4:遍历顺序,因为需要由dp[j-1],dp[j-2]推出,所以从前向后遍历
        if len(nums) == 0:
            return 0          # nums为空,直接返回0
        if len(nums) == 1:
            return nums[0]    # nums长度为1,只能偷一间,返回nums[0]
        dp = [0] * len(nums)      # 先将dp数组都初始化为0,长度就是nums的长度
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])
        for j in range(2, len(nums)):
            dp[j] = max(dp[j-2] + nums[j], dp[j-1])
        return max(dp)

二、LeetCode213. 打家劫舍 II

        1:题目描述(213. 打家劫舍 II)

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

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

        2:解题思路

        本题对于198. 打家劫舍来说,多了一个限制条件,就是最后一个房间是与第1个房间相连的,所以就可以分为:偷第一间房,就不能偷最后一间;偷最后一间房,就不能偷第一间房

        偷第一间房,就不能偷最后一间:可以将nums切割成不含最后一个元素的nums,进行求偷窃的最大金额

        偷最后一间房,就不能偷第一间房:就将nums切割成不含首个元素的nums,进行求偷窃的最大金额

        最后取两种情况下,偷窃金额最大的值作为返回结果

        其中在计算两种情况下的偷窃最大金额时,初始化的时候,只初始化dp[0],不初始化dp[1]?

        dp[1] = max(nums[0], nums[1])  

        为什么不能初始化dp[1],因为nums是去掉了首元素或尾元素的,如果原始的nums长度为2,去掉首元素或尾元素后,传入robRange()函数的nums长度为1,只有nums[0],此时使用dp[1] = max(nums[0], nums[1])会报错,因为找不到nums[1]

class Solution:
    def rob(self, nums: List[int]) -> int:
        # 分两种情况
        # 1:不偷第一间房
        # 2:不偷最后一间
        if len(nums) == 1:
            return nums[0]
        nums1 = self.robRange(nums[:len(nums)-1])       # 不偷最后一间房,得到的偷窃最大金额
        nums2 = self.robRange(nums[1:])                 # 不偷第一间房,得到的偷窃最大金额
        return max(nums1, nums2)           # 取两种情况中的最大金额

    def robRange(self, nums):
        # 1:确定dp数组的定义:dp[j]表示下标j(包括j)以内的房屋,最多可以偷窃的金额为dp[j]
        # 2:确定递推公式,分两种,j号房偷和不偷
        # j号房偷,dp[j] = dp[j-2]+nums[j]
        # j号房不偷,dp[j] = dp[j-1]
        # 要取偷窃的最大金额,所以dp[j]=max(dp[j-2]+nums[j], dp[j-1])
        # 3:初始化,根据递推公式,后面的需要由j-2,j-1推出,所以dp[0],dp[1]需要进行初始化
        # dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1])
        # 4:遍历顺序,因为需要由dp[j-1],dp[j-2]推出,所以从前向后遍历
        dp = [0] * len(nums)
        dp[0] = nums[0]
        # dp[1] = max(nums[0], nums[1])   
        # 为什么不能初始化dp[1],因为nums是去掉了首元素或尾元素的,如果原始的nums长度为2,去掉首元素或尾元素后,传入robRange()函数的nums长度为1,只有nums[0],此时使用dp[1] = max(nums[0], nums[1])会报错,因为找不到nums[1]
        for j in range(1, len(nums)):
            if j == 1:
                # j==1,偷1号房,取nums[j],不偷1号房,取dp[0],取两者中的最大值
                dp[j] = max(dp[0], nums[j])      
            else:
                dp[j] = max(dp[j-2]+nums[j], dp[j-1])
        return dp[-1]

三、LeetCode337. 打家劫舍 III

        1:题目描述(337. 打家劫舍 III)

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

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

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

        2:解题思路

        需要使用后序遍历进行遍历二叉树,关键是要讨论当前节点抢还是不抢。如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子

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

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

        递归函数的返回值就是dp数组

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

        2:确定终止条件

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

if node == None:
    return(0, 0)

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

        3:确定遍历顺序

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

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

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

# 向左递归
left = self.traversal(node.left) 
# 向右递归      
right = self.traversal(node.right) 

        4:确定单层递归的逻辑

        如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; 

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

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

# 向左递归
left = self.traversal(node.left) 
# 向右递归      
right = self.traversal(node.right)    

# 处理当前节点,偷与不偷
# 不偷当前节点,偷子节点
# 子节点,又分偷与不偷,取两个偷与不偷的最大值并相加,即为不偷当前节点的金额
val_0 = max(left[0], left[1]) + max(right[0], right[1])
# 偷当前节点,不偷子节点
# 偷窃的金额为:当前节点的金额+不偷子左节点的金额+不偷子右节点的金额
val_1 = node.val + left[0] + right[0]

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

代码如下:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        # dp数组(dp table)以及下标的含义:
        # 1. 下标为 0 记录 **不偷该节点** 所得到的的最大金钱
        # 2. 下标为 1 记录 **偷该节点** 所得到的的最大金钱
        dp = self.traversal(root)
        return max(dp)
    
    # 要用后序遍历, 因为要通过递归函数的返回值来做下一步计算
    def traversal(self, node):
        # 确定递归终止条件,遇到了空节点,肯定是不能偷的
        if node == None:
            return(0, 0)  
        # 向左递归
        left = self.traversal(node.left) 
        # 向右递归      
        right = self.traversal(node.right)    

        # 处理当前节点,偷与不偷
        # 不偷当前节点,偷子节点
        # 子节点,又分偷与不偷,取两个偷与不偷的最大值并相加,即为不偷当前节点的金额
        val_0 = max(left[0], left[1]) + max(right[0], right[1])
        # 偷当前节点,不偷子节点
        # 偷窃的金额为:当前节点的金额+不偷子左节点的金额+不偷子右节点的金额
        val_1 = node.val + left[0] + right[0]

        return (val_0, val_1)

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

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

相关文章

移动设备软件开发-广播机制

广播机制 1.广播机制概述 1.1生活中的广播机制 1.显示生活中的广播就比如说村里的大喇叭,车上的收音机接收的广播FM广播,学校里的校园广播都是常见的广播,安卓中的广播和生活中的广播是十分类似的。 1.2广播特点 发送者 多种广播方式实…

群晖外网访问终极解决方法:IPV6+阿里云ddns+ddnsto

写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法,这是后发现移动没有公网IP,只有ipv6(公网的),时候有小伙伴要问,要是没有ipv6就没办法访问群晖了吗? 不&…

吉时利KEITHELY2612B源表技术参数

作为2600B系列源表SMU系列产品的一部分,2612B源表SMU是全新改良版双通道SMU,具有紧密集成的4象限设计,能同步源和测量电压/电流以提高研发到自动生产测试等应用的生产率。除保留了2612A的全部产品特点外,2612B还具有6位半分辨率、…

Spring基础篇:高级注解编程

文章内容来自于B站孙哥说Spring第一章:Configuration一:配置Bean替换XML细节二:应用配置Bean工厂对象三:配置Bean细节分析1:整合Logback三:Component第二章:Bean一:Bean的使用1&…

Prometheus+Grafana部署

一 、Prometheus 源码安装和启动配置 普罗米修斯下载网址:https://prometheus.io/download/ 监控集成器下载地址:http://www.coderdocument.com/docs/prometheus/v2.14/instrumenting/exporters_and_integrations.html 1.实验环境 IP角色系统172.16.1…

理解浅拷贝和深拷贝以及实现方法

一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和引用数据类型Object,包含(function,Array,Date)。 1、基本数据类型的特点:直接存储在栈内存中的数据 …

品牌投资与形象全面升级 | 快来认识全新的 Go 旅城通票

近日,Go 旅城通票(Go City)品牌全面升级,旨在提高旅游爱好者对品牌的认知。从新冠疫情大流行中阴霾中走出来的 Go 旅城通票复苏势头强劲,专注于技术提升,使命是协助旅游爱好者无论到世界各地的哪一个城市畅…

在线分析网站日志软件-免费分析网站蜘蛛的软件

搜索引擎蜘蛛的作用是什么?我们网站上的内容如果要想被搜索引擎收录并且给予排名,就必须要经过搜索引擎蜘蛛的爬取并且建立索引。所以让搜索引擎蜘蛛更好的了解我们的网站是很重要的一步!搜索引擎蜘蛛在爬取某个网站,是通过网站的…

浅谈虚拟地址转换成物理地址(值得收藏)

这里,我们讲解一下Linux是如何将虚拟地址转换成物理地址的 一、地址转换 在进程中,我们不直接对物理地址进行操作,CPU在运行时,指定的地址要经过MMU转换后才能访问到真正的物理内存。 地址转换的过程分为两部分,分段…

Linux systemctl 详解自定义 systemd unit

Linux systemctl 详解&自定义 systemd unit systemctl 序 大家都知道,我们安装了很多服务之后,使用 systemctl 来管理这些服务,比如开启、重启、关闭等等,所以 systemctl 是一个 systemd 系统。centos 使用 systemctl 来代…

9.8 段错误,虚拟内存,内存映射 CSAPP

相信写代码的或多或少都会遇到段错误,segmentation fault. 今天终于看到这里面的底层原理 参考: https://greenhathg.github.io/2022/05/18/CMU213-CSAPP-Virtual-Memory-Systems/18-Virtual-Memory-SystemsSimple memory system exampleAddress Trans…

(转)CSS结合伪类实现icon

老规矩,还是先说说业务场景:有一个图片列表,可以添加、删除和更改,其中呢删除时设计给的设计稿时悬浮(hover)在图片上时显示删除的图标,所以就有了这个用before实现icon的场景 进入正文&#xf…

嵌入式系统开发笔记108:IO的使用方法与面向对象程序设计

文章目录前言一、IO引脚的基本概念二、映射层的设置1、映射层是原理图的直译层2、IO引脚的设置在hal.h 和 hal.cpp文件中完成(1)在hal.h中进行类定义(2)在hal.cpp中完成引脚映射三、面向对象程序设计思想1、程序设计分类2、举例3、…

DevExpress之C#界面+MATLAB动态链接库联合编程

MATLAB导出动态链接库 在MATLAB命令行中输入:deploytool,打开如下界面,选择Library Compiler 对于C#,选择.NET Assembly,点击右侧的“+”加号,添加要导出的函数 可添加多个函数 下面的类名中输入即为导出后类的名称 点击设置按钮,输入参数-C,参数的具体含义如下 …

简化MRO工业品供采交易路径,S2B2B商城助力企业构建业务一体化管理优势

在政策拉动、市场需求驱动及数字技术进步等多重力量共同作用下,近5年来,我国工业品B2B市场规模保持上升的态势。尽管2022年受疫情反复影响,但中国经济向好的局面并未改变,中国数字化经济依然会加快工业品B2B市场的发展&#xff0c…

绿色债券数据集2016-2021(含交易代码、债券简称、发行规模期限等多指标数据)

1、数据来源:wind 2、时间跨度:2016.01-2021.11年 3、区域范围:全国 4、指标说明: 部分指标如下: 交易代码 债券简称 发行起始日 缴款日 计划发行规模(亿) 发行金额上…

第四章. Pandas进阶—时间序列

第四章. Pandas进阶 4.9 时间序列 1.重采样(resample) 在Pandas中,对时间序列频率的调整称为重采样,即时间序列从一个频率转换到另一个频率的过程,由周统计变成月统计 1).语法: 4.8章 第4点 已介绍过&…

5G无线技术基础自学系列 | MIMO功能

素材来源:《5G无线网络规划与优化》 一边学习一边整理内容,并与大家分享,侵权即删,谢谢支持! 附上汇总贴:5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 无线通信的迅速发展对系统的容量和频谱…

天启星座(Tianqi)介绍

天启星座(Tianqi)由38颗卫星组网而成,提供全球短数据采集。致力于为全球物联网相关行业用户提供覆盖全球、准实时的物联网卫星数据服务,真正实现空中、海洋和地面的万物互联,构建天地一体化的卫星物联网生态系统&#…

stm32 笔记 UART读取及HAL库应用

基本流程图 由此图可知: 采用HAL库,中断方式接收串口,只有当RxXferCount 0 时,也就是调用这个函数,接收指定量的数据大小完成时,才会调用回调函数HAL_UART_RxCpltCallback()。 而且,RxXferCou…