2024 7/13 七月就要过去一半了,伴随着蝉鸣,酷暑被窗户隔离,我坐在凳子上,有一只蚊子不愿意放过我。
1、题目描述
2、算法分析
前面做过打家劫舍Ⅱ,今天来做打家劫舍Ⅲ,小偷也是越来越聪明了,意识到房子的排列类似二叉树,这小偷之前大概率是程序员,我就说吧,计算机就业就是这么广。JAVA炒粉、JAVA烧烤、JAVA外卖、JAVA探花也是应运而生。
Anyway,让我们来分析这题。
这题啊数据结构就是二叉树,只不过除了根节点,其他父节点就只有一个儿子。两个连着的不能都偷,怎么偷才能偷最多米呢?
算法步骤
- 初始化:创建两个哈希表
f
和g
,用于存储每个节点的最大价值(偷或不偷)。 - 深度优先搜索(
DFS
): 对每个节点,首先递归地处理其左子树和右子树。 更新f[node]
:如果偷取当前节点node
,则当前节点的价值加上不偷取左右子节点时左右子树的最大价值(即g[node.left]
和g[node.right]
)。
更新g[node]
:不偷取当前节点node
时,取偷取或不偷取左子节点的较大值加上偷取或不偷取右子节点的较大值(即
Math.max(f[node.left], g[node.left]) + Math.max(f[node.right],g[node.right]))
。 - 返回结果:在遍历完整棵树后,
f[root]
和g[root]
分别表示偷取根节点和不偷取根节点时的最大价值。最终答案取两者中的较大值,即Math.max(f.getOrDefault(root,0), g.getOrDefault(root, 0))
。
3、代码
// f[node]表示偷当前节点node时的最大金额
Map<TreeNode, Integer> f = new HashMap<TreeNode, Integer>();
// g[node]表示不偷当前节点node时的最大金额
Map<TreeNode, Integer> g = new HashMap<TreeNode, Integer>();
// 主函数,返回偷窃二叉树能获得的最大金额
public int rob(TreeNode root) {
// 执行深度优先搜索来填充f和g的值
dfs(root);
// 返回偷或不偷根节点时的较大值,因为小偷可以选择从根节点开始偷或不偷
return Math.max(f.getOrDefault(root, 0), g.getOrDefault(root, 0));
}
// 深度优先搜索函数,用于计算每个节点的f和g值
public void dfs(TreeNode node){
// 如果节点为空,则直接返回
if(node == null){
return;
}
// 递归计算左子树的f和g值
dfs(node.left);
// 递归计算右子树的f和g值
dfs(node.right);
// 计算当前节点node偷时的最大金额:节点值加上不偷左右子节点时的最大金额
f.put(node, node.val + g.getOrDefault(node.left, 0) + g.getOrDefault(node.right, 0));
// 计算当前节点node不偷时的最大金额:左右子节点偷与不偷的较大值之和
g.put(node, Math.max(f.getOrDefault(node.left, 0), g.getOrDefault(node.left, 0)) +
Math.max(f.getOrDefault(node.right, 0), g.getOrDefault(node.right, 0)));
}
4、复杂度分析
- 时间复杂度
O(n)
; - 空间复杂度
O(n)
:由于递归会使用到栈空间,空间代价是 O(n),哈希表的空间代价也是 O(n),故空间复杂度也是 O(n)。
okok,做完了,拜拜啦!