数据结构与算法之 leetcode 动态规划打家劫舍 III 树结构问题

数据结构与算法阿木 发布于 2025-07-12 6 次阅读


动态规划打家劫舍 III:树结构问题解析与代码实现

动态规划(Dynamic Programming,DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中广泛应用的方法。它通过将复杂问题分解为更小的子问题,并存储这些子问题的解以避免重复计算,从而提高算法的效率。在LeetCode等编程竞赛平台中,动态规划问题尤为常见,其中树结构问题更是考验程序员对DP算法的掌握程度。

本文将围绕LeetCode上的“打家劫舍 III”问题,深入解析树结构动态规划问题的解题思路,并给出相应的代码实现。

问题分析

“打家劫舍 III”问题描述如下:

假设有一个二叉树,每个节点的值代表该节点的房间内存放的金币数量。小偷每次只能偷一个节点的金币,但相邻的节点不能同时被偷。求小偷从这些房间中偷到的最大金币数量。

解题思路

对于树结构动态规划问题,我们可以采用以下步骤:

1. 定义状态:定义一个状态表示从当前节点开始,小偷能偷到的最大金币数量。

2. 状态转移方程:根据子节点的状态,推导出当前节点的状态。

3. 边界条件:确定递归的终止条件。

4. 记忆化:使用记忆化技术避免重复计算。

定义状态

对于每个节点,我们可以定义两个状态:

- `rob(node)`: 如果偷取当前节点,则不能偷取其子节点,此时能偷到的最大金币数量。

- `not_rob(node)`: 如果不偷取当前节点,则可以自由选择是否偷取其子节点,此时能偷到的最大金币数量。

状态转移方程

根据上述定义,我们可以得到以下状态转移方程:

- `rob(node) = node.val + not_rob(node.left) + not_rob(node.right)`

- `not_rob(node) = max(rob(node.left), not_rob(node.left)) + max(rob(node.right), not_rob(node.right))`

边界条件

当节点为空时,`rob(node)` 和 `not_rob(node)` 都为 0。

记忆化

为了避免重复计算,我们可以使用一个字典来存储每个节点的状态。

代码实现

以下是用Python实现的代码:

python

class TreeNode:


def __init__(self, val=0, left=None, right=None):


self.val = val


self.left = left


self.right = right

def rob(root):


memo = {}

def dfs(node):


if not node:


return 0, 0


if node in memo:


return memo[node]

left_rob, left_not_rob = dfs(node.left)


right_rob, right_not_rob = dfs(node.right)

rob_current = node.val + left_not_rob + right_not_rob


not_rob_current = max(left_rob, left_not_rob) + max(right_rob, right_not_rob)

memo[node] = (rob_current, not_rob_current)


return memo[node]

return max(dfs(root))

测试代码


root = TreeNode(3, TreeNode(2, TreeNode(3), TreeNode(3)), TreeNode(3, TreeNode(1), TreeNode(3)))


print(rob(root)) 输出:7


总结

本文通过分析“打家劫舍 III”问题,介绍了树结构动态规划问题的解题思路和代码实现。在解决这类问题时,我们需要定义状态、推导状态转移方程、确定边界条件和记忆化,从而提高算法的效率。希望本文能帮助读者更好地理解和掌握树结构动态规划问题。