【小白友好】LeetCode 打家劫舍 III

https://leetcode.cn/problems/house-robber-iii/description/

前言

建议还是先看看动态规划的基础题再看这个。动态规划是不刷题,自己100%想不出来的
基础题:

  • 最大子数组和
  • 乘积最大子数组
  • 最长递增子序列
  • 最大升序子数组和

小白想法

现在我们想遍历的数据结构不是数组了,而是一颗树。在树上的dp叫做“树形dp”(so called)。
那在遍历寻找解更新dp的时候无非就是用遍历树的dfs那一套了,不再是for i in range(length)了。

现在不方便用dp数组去存储dp的值了,毕竟这是个树。那用什么?我们之前之所以用数组,是因为使用dp[i]能够直接取到遍历到nums[i]时dp的值,之前的dp数组实际上充当的是一个hash table(字典、map…随便你怎么叫)的作用。 那我们就直接用字典就好啦!不再用数组了。

然后能不能像for i in range(length)一样“从前往后”边遍历边更新?树上的“从前往后”是什么?是从叶子走向根

有了前几点的铺垫,下面正式进入状态转移方程的构建。

还能不能像以前“打家劫舍”那个题一样通过选择前几个状态来更新dp?
看上去好像可以。但是请注意我们现在的“dp数组”不是数组了,而是一个字典。字典的key是node,value是dp值。如果是数组,我们可以很方便的通过i-1,i-2指针来访问前面的状态,但是此时的字典已经不适合了。 当前的状态i和前面时刻-2的“联系”已经没有了。

题外话,莫非我可以先把“树形”的数据先压成一个数组,然后再套用之前的思路?想法很好,甚至也能做,但是写起来比较麻烦,下一个!

就算是有联系,在当前节点时,需要考虑 自己2个孩子是否被偷,没偷就有可能跟孙子一起被偷,共计6个节点,我相信写起来也挺复杂,要max很多次。

dead end after all…

题解,学习

换个dp数组的意义吧!我们不拘泥于过去的思路了。
在之前做“最大连乘子数组”的时候我们不是也用了2个dp数组吗?

把dp拆开成2个,分成 选择了子节点的最大值,和没选择了子节点的最大值。请注意树上的“从前往后”是从叶子到根。把这2个dp分别叫做dp_selecteddp_unselected

先考虑最简单的情况,从最前面开始,只有一个叶子的时候,2个dp的值很好知道,叶子值或者0。

而当到了树上的分支now的时候,dp_selected[now]的意义是选择当前节点,那么应该等于自己当前节点的值+没选自己孩子的最大dp值:now.val+dp_unselected[now.left]+dp_unselected[now.right],只有这 一种情况,是因为相邻的父子不能一起选;而dp_unselected[now]就比较多了:由于我当前节点没选择,那么我的孩子可以选,也可以不选,那我要当前最大的,要取他们的最大值更新。

上面写的比较“大白话”,我个人感觉用数学公式表达起来比较简洁易懂。。

以下为官方题解
在这里插入图片描述

接下来就是遍历,然后通过转移方程一边遍历一边更新就行。

代码

# 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的意义变了,不再是“以xx结尾的结果”from collections import defaultdictdp_selected,dp_unselected=defaultdict(int),defaultdict(int)def travel_tree(now):if now is None:return travel_tree(now.left)travel_tree(now.right)left=now.leftright=now.rightres1=now.val+dp_unselected[left]+dp_unselected[right]res2=max(dp_selected[left],dp_unselected[left])+max(dp_selected[right],dp_unselected[right])dp_selected[now]=res1# 可以选择不更新,因为不更新他就是0# 在节点选择的过程中,0是肯定会被丢弃的dp_unselected[now]=res2return travel_tree(root)return max(dp_selected[root],dp_unselected[root])

defaultdict的作用是给予在dict中不存在的key一个初始值,简化代码。

提交,过了。

想不出来,不刷题真的想不出来。

优化

显然dp的hash table是很重要的,但是我们能不能把他也优化掉,不使用他的额外的空间呢?
我们可以从转移方程看到,当前点的更新只跟自己孩子的那几个值有关。

这熟悉的配方!在dp[i]只跟dp[i-1]有关时,我们也能通过类似的方法只保留上一个dp的值来优化空间。

那么怎么只保留上一个dp的值呢?我们上一个dp的值是“选左孩子的最大值”、“不选左孩子的最大值”、“选右孩子的最大值”、“不选右孩子的最大值”,一共4个,使用函数返回给父节点就可以了!(毕竟除了使用额外的空间,函数返回值是唯一父子能交流的东西了)

class Solution:def rob(self, root: Optional[TreeNode]) -> int:def travel_tree(now):if now is None:  return 0, 0  l_rob, l_not_rob = travel_tree(now.left)r_rob, r_not_rob = travel_tree(now.right)rob = l_not_rob + r_not_rob + node.val  # 选not_rob = max(l_rob, l_not_rob) + max(r_rob, r_not_rob)  # 不选return rob, not_robreturn max(travel_tree(root))  # 根节点选或不选的最大值

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

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

相关文章

云桥通+跨境电商:SDWAN企业组网优化跨境网络案例

跨境电商企业在全球范围内展开业务,需构建稳定高效的网络架构以支持其电商平台运营。云桥通SDWAN企业组网技术为跨境电商提供网络连接和管理的优化,提升网络性能、可靠性和安全性。以下是一家跨境电商企业的SDWAN组网案例,详细介绍其实施情况…

命名实体识别NER(综合代码示例)

一、命名实体识别发展方向 二、中文数据集 CCKS2017开放的中文的电子病例测评相关的数据。 评测任务一:https://biendata.com/competition/CCKS2017_1/ 评测任务二:https://biendata.com/competition/CCKS2017_2/ CCKS2018开放的音乐领域的实体识别任务…

【Qt】Sqlite数据库加密

1. 加密方式 对数据库文件加密。既不会暴露表结构,也不会暴露数据细节。 2. 加密工具(QtCipherSqlitePlugin) 用于密码 SQLite 的 Qt 插件,它基于 SQLite 源和 wxWidget 中的 wxSQLite3插件github地址:https://gith…

重读 Java 设计模式: 探索经典之道与 Spring 框架的设计

写在开头 记得大学刚毕业那会儿,想学点东西,于是拿出了《Head First 设计模式》这本书,就开始了阅读,我曾对这些模式感到晦涩难懂。然而,随着工作岁月的增长,我逐渐领悟到设计模式的价值,尤其是…

2024高频前端面试题 JavaScript 和 ES6 篇

HTML和CSS篇: 2024高频前端面试题 HTML 和 CSS 篇-CSDN博客 一. JavaScript篇 1. 数据类型有哪些 1) 基本数据类型 数值(Number)、字符串(String)、布尔值(Boolean)、Undefined、Null、Symbol、BigInt 2) 引用数据类型 对象(Object)、数组(Array)、函数(Funct…

为啥要用C艹不用C?

在很多时候,有人会有这样的疑问 ——为什么要用C?C相对于C优势是什么? 最近两年一直在做Linux应用,能明显的感受到C带来到帮助以及快感 之前,我在文章里面提到环形队列 C语言,环形队列 环形队列到底是怎么回…

keycloak-keycloak部署启动及打开调试日志

一、问题描述 keycloak截止目前已更新到23.0.7版本,好多网上关于keycloak教程都停留在15版本之前,有一些地方版本变化较大,计划写一个系列来记录keycloak使用。本文主要记录keycloak启动及打开调试日志的方法。 本文keycloak实验版本为23.0…

MySQL的初学者教程—Navicat的基本操作方法

MySQL的初学者教程—Navicat的基本操作方法 1、运行Navicat 双击桌面的Navicat 12 for MySQL。 2、新建MySQL连接 点击【测试连接】。 zyyMySQL的连接创建成功! 3、新建数据库 4、新建表 点击【保存】 表【usermanage】建好了。 点【usermanage】的鼠标右键&#…

PDA主要用来做什么?

PDA,可以满足信息采集、信息处理、信息查询的需求,实现信息的一体化管理,帮助企业迎来无纸化操作时代,减少失误、提高效率、提升企业竞争力,并最终赢得市场。主要具备以下几个功能:1、条码扫描条码扫描是手…

【蓝牙协议栈】【BR/EDR】【AVRCP】AVRCP常用指令介绍

1. AVRCP常用指令协议栈介绍 1.1 AVRCP Play 播放蓝牙音乐,使用AVCTP的AV/C格式的命令走控制通道 1.2 AVRCP Pause 暂停蓝牙音乐,同播放指令一样使用AV/C格式的命令走控制通道 实际使用中由于CT端可能会快速发送两次播放或暂停指令,会触发…

【活动】金三银四,前端工程师如何把握求职黄金期

随着春意盎然的气息弥漫大地,程序员群体中也迎来了一年一度的“金三银四”求职热潮。这个时间段对于广大前端工程师而言,不仅象征着生机勃发的新起点,更是他们职业生涯中至关重要的转折点。众多知名公司在这一时期大规模开启招聘通道&#xf…

uniapp问卷调查(单选)

前言 该代码片段只支持问卷调查的单选功能 使用组件库 配置 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 (uviewui.com) 代码 <template> <view> <view v-for"(item, index) in radiolist1" :key"index"> …