Loading

[LeetCode] 337. House Robber Ⅲ(偷家贼之三)

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 要不要偷成为问题的一个分水岭:

  1. root 要偷,意味着 root 的子节点不能偷,你只能偷 root 的子节点的子节点

  2. 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
    }
}
posted @ 2020-11-13 09:24  Zhongju.copy()  阅读(48)  评论(0编辑  收藏  举报