资讯详情

LeetCode 1130:动态规划与贪心算法解叶值最小代价生成树

📅 2026/10/26 9:25:00 | 华诺云谱 👁 阅读
LeetCode 1130:动态规划与贪心算法解叶值最小代价生成树
1. 问题背景与核心概念解析今天我们来拆解一道经典的LeetCode动态规划题目——1130.叶值的最小代价生成树。这道题在2023年的算法面试中频繁出现尤其在考察候选人对树形结构和动态规划的综合理解能力时经常被选用。题目给定一个正整数数组arr其中每个元素代表一棵二叉树的叶子节点值。我们需要根据这些叶节点构建非叶子节点使得整棵树的代价最小。这里的代价定义为每个非叶子节点的值等于其左右子树中最大叶节点值的乘积所有非叶子节点的值之和就是整棵树的代价。举个例子对于输入数组arr [6,2,4]最优的构建方式如下24 / \ 12 4 / \ 6 2计算过程第一个非叶子节点12 6 * 2第二个非叶子节点24 max(6,2) * 4 6 * 4总代价 12 24 362. 动态规划解法深度剖析2.1 状态定义与转移方程这道题的核心在于发现其具有最优子结构特性——整个树的最小代价取决于其子树的最小代价。我们定义dp[i][j]表示子数组arr[i...j]对应的最小代价那么状态转移方程为dp[i][j] min(dp[i][k] dp[k1][j] max(arr[i..k]) * max(arr[k1..j])) 其中i ≤ k j这个方程的含义是对于区间[i,j]我们尝试所有可能的分割点k将数组分为左右两部分分别计算左右子树的最小代价再加上当前根节点的值左右子树最大叶节点的乘积。2.2 实现细节与优化技巧在实际编码实现时有几个关键点需要注意初始化当i j时dp[i][j] 0单个叶节点没有代价遍历顺序需要按照区间长度从小到大计算确保子问题先被解决最大值预处理可以预先计算max[i][j]表示arr[i...j]的最大值避免重复计算这里给出Python实现的关键代码片段def mctFromLeafValues(arr): n len(arr) dp [[float(inf)] * n for _ in range(n)] max_val [[0] * n for _ in range(n)] for i in range(n): max_val[i][i] arr[i] dp[i][i] 0 for length in range(2, n1): for i in range(n - length 1): j i length - 1 for k in range(i, j): dp[i][j] min(dp[i][j], dp[i][k] dp[k1][j] max_val[i][k] * max_val[k1][j]) max_val[i][j] max(max_val[i][j-1], arr[j]) return dp[0][n-1]2.3 复杂度分析时间复杂度O(n^3)因为有三重循环区间长度、起始点、分割点 空间复杂度O(n^2)用于存储dp和max_val表3. 贪心算法的巧妙解法3.1 单调栈的应用这道题其实有一个更优的O(n)解法利用了贪心思想和单调栈。核心观察是为了最小化总代价我们应该尽早合并较小的叶节点因为较小的值参与乘积的次数越多总代价越大。具体步骤维护一个单调递减栈遍历数组当前元素比栈顶大时弹出栈顶元素并与相邻较小者合并最后处理栈中剩余元素Python实现def mctFromLeafValues(arr): res 0 stack [float(inf)] for num in arr: while stack[-1] num: mid stack.pop() res mid * min(stack[-1], num) stack.append(num) while len(stack) 2: res stack.pop() * stack[-1] return res3.2 贪心算法的正确性证明为什么这个贪心策略有效关键在于每次合并都选择当前最小的可能乘积。当遇到一个较大的数时它会遮挡之前较小的数使得这些较小的数不会再与更远的数相乘。这与动态规划中每个子问题都做出局部最优选择的思想一致。4. 典型错误与调试技巧4.1 边界条件处理在实现过程中有几个常见的陷阱需要注意数组长度为1时应该返回0单调栈解法需要预先放入一个极大值作为哨兵动态规划解法中k的取值范围是[i, j-1]4.2 调试建议当你的代码出现错误时可以尝试以下调试方法打印dp表观察计算顺序是否正确对于小规模测试用例如[6,2,4]手动计算dp表进行对比检查max_val表的计算是否正确重要提示在单调栈解法中务必确保栈底有一个极大值哨兵否则可能在弹出元素时出现空栈错误。5. 同类问题扩展与变种这道题属于最优二叉搜索树问题的一个变种。类似的问题还包括矩阵链乘法问题多边形三角剖分问题哈夫曼编码问题这些问题的共同特点是都具有最优子结构性质都可以用动态规划解决。理解它们之间的联系可以帮助你建立更系统的动态规划解题思维。6. 实际应用场景虽然这个问题看起来是纯理论的但它实际上有一些有趣的实际应用编译器优化中的表达式树生成数据库查询计划优化图像处理中的金字塔算法理解这类问题的解法可以帮助你在面对实际工程中的优化问题时能够识别出其中的模式并应用相应的算法。7. 性能优化进阶对于特别大的输入规模n1000O(n^3)的动态规划解法就不适用了。此时优先考虑贪心解法如果必须用DP可以尝试记忆化搜索剪枝寻找问题特定的数学性质进行优化在面试场景中通常n100因此掌握基础的DP解法就足够了。但了解更优解可以展现你的算法深度。8. 个人解题心得在实际解决这个问题时我有几点深刻体会先从小规模例子入手手动计算几个案例这往往能揭示问题本质动态规划问题的关键在于准确的状态定义和转移方程不要忽视贪心算法的可能性特别是当问题具有局部最优导致全局最优的特性时栈结构在处理数组区间问题时往往有意想不到的效果这道题的价值在于它综合考察了对树结构、动态规划和贪心算法的理解。我建议至少用两种方法实现它比较它们的异同这样能获得更深入的理解。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑