树形动态规划(Tree DP)算法详解

树形动态规划(Tree DP)算法详解

LeetCode-Py ⛽️「算法通关手册」:超详细的「算法与数据结构」基础讲解教程,从零基础开始学习算法知识,800+ 道「LeetCode 题目」详细解析,200 道「大厂面试热门题目」。 LeetCode-Py 项目地址: https://gitcode.com/gh_mirrors/le/LeetCode-Py

什么是树形动态规划

树形动态规划(Tree DP)是一种在树形结构上进行推导的动态规划方法。与常规动态规划不同,树形DP的求解过程通常以节点从深到浅(子树从小到大)的顺序作为动态规划的"阶段"。

树形DP问题的特点:

  1. 数据结构为树形结构(二叉树或多叉树)
  2. 状态转移发生在父子节点之间
  3. 通常采用深度优先搜索(DFS)进行遍历

树形DP的分类

按转移方向分类

  1. 自底向上:通过递归先求解子树,然后在回溯时自底向上进行状态转移
  2. 自顶向下:从根节点开始向下递归,逐层计算子节点状态(较少使用)

按根节点是否固定分类

  1. 固定根的树形DP:指定了根节点,通常只需一次DFS
  2. 不定根的树形DP:根节点不固定,通常需要两次DFS(预处理+换根)

固定根的树形DP实例

二叉树中的最大路径和

问题描述:给定一个二叉树,找到路径和最大的路径,路径可以不经过根节点。

解题思路

  1. 定义递归函数计算每个节点的最大贡献值
  2. 维护全局最大路径和变量
  3. 节点最大贡献值 = 当前节点值 + max(左子树贡献, 右子树贡献)
  4. 全局最大路径和 = max(全局最大, 左贡献+右贡献+当前值)
class Solution:
    def __init__(self):
        self.ans = float('-inf')
        
    def dfs(self, node):
        if not node:
            return 0
        left_max = max(self.dfs(node.left), 0)
        right_max = max(self.dfs(node.right), 0)

        cur_max = left_max + right_max + node.val
        self.ans = max(self.ans, cur_max)

        return max(left_max, right_max) + node.val

    def maxPathSum(self, root):
        self.dfs(root)
        return self.ans

相邻字符不同的最长路径

问题描述:在树中找到最长路径,要求路径上相邻节点字符不同。

解题思路

  1. 构建树结构(邻接表)
  2. 定义DFS函数计算每个节点的最长路径
  3. 维护全局最长路径变量
  4. 只有当相邻节点字符不同时才更新路径长度
class Solution:
    def longestPath(self, parent, s):
        size = len(parent)
        graph = [[] for _ in range(size)]
        for i in range(1, size):
            graph[parent[i]].append(i)

        ans = 0
        def dfs(u):
            nonlocal ans
            u_len = 0
            for v in graph[u]:
                v_len = dfs(v)
                if s[u] != s[v]:
                    ans = max(ans, u_len + v_len + 1)
                    u_len = max(u_len, v_len + 1)
            return u_len

        dfs(0)
        return ans + 1

不定根的树形DP实例

最小高度树

问题描述:找到树中所有能使树高度最小的根节点。

解题思路

  1. 第一次DFS:自底向上计算每个节点向下走的最长和次长路径
  2. 第二次DFS:自顶向下计算每个节点向上走的最长路径
  3. 综合向上和向下路径得到每个节点作为根时的树高
  4. 找出所有最小高度的根节点
class Solution:
    def findMinHeightTrees(self, n, edges):
        graph = [[] for _ in range(n)]
        for u, v in edges:
            graph[u].append(v)
            graph[v].append(u)
        
        down1 = [0] * n  # 最长向下路径
        down2 = [0] * n  # 次长向下路径
        p = [0] * n      # 记录最长路径的子节点
        
        def dfs(u, fa):
            for v in graph[u]:
                if v == fa:
                    continue
                dfs(v, u)
                height = down1[v] + 1
                if height >= down1[u]:
                    down2[u] = down1[u]
                    down1[u] = height
                    p[u] = v
                elif height > down2[u]:
                    down2[u] = height
        
        up = [0] * n  # 最长向上路径
        def reroot(u, fa):
            for v in graph[u]:
                if v == fa:
                    continue
                if p[u] == v:
                    up[v] = max(up[u], down2[u]) + 1
                else:
                    up[v] = max(up[u], down1[u]) + 1
                reroot(v, u)
        
        dfs(0, -1)
        reroot(0, -1)
        
        min_h = min(max(down1[i], up[i]) for i in range(n))
        return [i for i in range(n) if max(down1[i], up[i]) == min_h]

树形DP的复杂度分析

对于大多数树形DP问题:

  • 时间复杂度:O(n),n为节点数,每个节点只被访问常数次
  • 空间复杂度:O(n),用于存储树结构和DP状态

总结

树形动态规划是处理树结构问题的有力工具,掌握它需要:

  1. 理解树的基本遍历方式(DFS为主)
  2. 学会设计合理的状态表示
  3. 掌握状态转移方程的推导
  4. 熟悉固定根和不定根两种情况的处理方法

通过本文的几个经典例题,读者可以逐步掌握树形DP的核心思想和解题技巧,为解决更复杂的树形问题打下坚实基础。

LeetCode-Py ⛽️「算法通关手册」:超详细的「算法与数据结构」基础讲解教程,从零基础开始学习算法知识,800+ 道「LeetCode 题目」详细解析,200 道「大厂面试热门题目」。 LeetCode-Py 项目地址: https://gitcode.com/gh_mirrors/le/LeetCode-Py

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邱行方Mountain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值