[LeetCode] 337. House Robber Ⅲ(偷家贼之三)
-
Difficulty: Medium
-
Related Topics: Tree, Depth-first Search
Description
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
这个贼发现他被传送到了一个新地方继续完成他的偷家大业。这个区域只有一个入口,叫做 "root"。除了 "root" 外,每座房子有且仅有一个父房子。游历一番之后,这个聪明的贼发现这片区域的所有房子组成了一棵二叉树。和上次一样,任意两个直接相连的房间如果同时被破门而入,则会自动报警。
Determine the maximum amount of money the thief can rob tonight without alerting the police.
计算这个贼在不惊动警察的情况下能偷得的钱的最大值。
Example
Example 1
Input: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
Output: 7
Explanation: Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2
Input: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
Output: 9
Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.
Solution
看到二叉树的题目,第一反应是能不能把它分解成更小规模的子问题求解。也就是说,rob(root)
能不能通过 rob(root.left)
和 rob(root.right)
(或者其它调用)通过一系列运算得出。递归调用,就需要知道两点:递归的出口在哪;以及递归关系。
递归出口不难找,当 root == null
时便是递归出口,此时返回 0
。
递归关系怎么找?由于相邻两节点不能偷,因此 root
要不要偷成为问题的一个分水岭:
-
root
要偷,意味着root
的子节点不能偷,你只能偷root
的子节点的子节点 -
root
不偷,意味着你拿不到root
里的钱,但你可以偷root
的子节点
二者都明确了,代码也就呼之欲出了:
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/
import kotlin.math.max
class Solution {
fun rob(root: TreeNode?): Int {
if (root == null) {
// 递归终止条件
return 0
}
// 两种情况,取决于 `root` 要不要偷
// - 偷 `root` -> 只能偷 `root` 的子节点的子节点
// - 不偷 `root` -> 可以偷 `root` 的子节点
// 以上二者取最大值
return max(
// 偷 `root`
root.`val` + rob(root.left?.left) + rob(root.left?.right) + rob(root.right?.left) + rob(root.right?.right),
// 不偷 `root`
rob(root.left) + rob(root.right)
)
}
}
这个代码能通过评测,但时间较慢,猜测是递归调用中出现了大量重复计算,尝试使用备忘模式,效果拔群:
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/
import kotlin.math.max
class Solution {
private val memo = hashMapOf<TreeNode, Int>()
fun rob(root: TreeNode?): Int {
if (root == null) {
// 递归终止条件
return 0
}
if (memo.containsKey(root)) {
return memo.getValue(root)
}
// 两种情况,取决于 `root` 要不要偷
// - 偷 `root` -> 只能偷 `root` 的子节点的子节点
// - 不偷 `root` -> 可以偷 `root` 的子节点
// 以上二者取最大值
val result = max(
// 偷 `root`
root.`val` + rob(root.left?.left) + rob(root.left?.right) + rob(root.right?.left) + rob(root.right?.right),
// 不偷 `root`
rob(root.left) + rob(root.right)
)
memo[root] = result
return result
}
}