LeetCode337 打家劫舍III
题目
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 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
若算root,则为root值+左右孙子节点
若不算root,则为左右儿子节点的最大值
由于计算孙子节点在当前root和儿子节点计算两边,为简便计算用map存储已计算的节点,若不存储计算超时
- 时间复杂度:O()
- 空间复杂度:O()
class Solution {
Map<TreeNode, Integer> cache = new HashMap<>();
public int rob(TreeNode root) {
if (root == null) {
return 0;
}
// 是否已经计算过
if (cache.containsKey(root)) {
return cache.get(root);
}
// 策略1:抢当前节点和孙子节点
int sum1 = root.val +
// 左子节点的子节点们
(root.left == null ? 0 : (rob(root.left.left) + rob(root.left.right))) +
// 右子节点的子节点们
(root.right == null ? 0 : (rob(root.right.left) + rob(root.right.right)));
// 策略2:只抢子节点
int sum2 = rob(root.left) + rob(root.right);
// 找出更大的值
int sum = Math.max(sum1, sum2);
// 并记录
cache.put(root, sum);
return sum;
}
}
动态规划2
记录当前节点算与不算的值
若算root,则为当前节点的值+左子树不算+右子树不算
若不算root,则为左子树算与不算的最大值+右子树算与不算的最大值
动态规划2-哈希表
- 时间复杂度:O(n)
- 空间复杂度:O(n)
class Solution {
private Map<TreeNode,Integer> f = new HashMap<>();//选root
private Map<TreeNode,Integer> g = new HashMap<>();//不选root
public int rob(TreeNode root) {
dfs(root);
return Math.max(f.getOrDefault(root,0),g.getOrDefault(root,0));
}
private void dfs(TreeNode root){
if(root==null){
return;
}
dfs(root.left);
dfs(root.right);
f.put(root,root.val+g.getOrDefault(root.left,0)+g.getOrDefault(root.right,0));
g.put(root,Math.max(f.getOrDefault(root.left,0),g.getOrDefault(root.left,0))+Math.max(f.getOrDefault(root.right,0),g.getOrDefault(root.right,0)));
}
}
动态规划2-数组
- 时间复杂度:O(n)
- 空间复杂度:O(n)
class Solution {
public int rob(TreeNode root) {
int[] res = dfs(root);
return Math.max(res[0],res[1]);
}
private int[] dfs(TreeNode root){
if(root==null){
return new int[]{0,0};
}
int[] res = new int[2];
int[] left = dfs(root.left);
int[] right = dfs(root.right);
//选root,根节点的值+左子树不选最大值+右子树不选最大值
res[0] = root.val + left[1]+right[1];
//不选root,左右子树的最大值相加
res[1] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
return res;
}
}
分类:
LeetCode Hot100
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理