news 2026/10/15 9:13:24

二叉树分割最大乘积:力扣1339题解析与优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二叉树分割最大乘积:力扣1339题解析与优化

1. 题目背景与核心问题解析

今天我们来拆解一道来自力扣平台的二叉树题目——1339号题"分裂二叉树的最大乘积"。这是一道典型的树形结构结合数学优化的题目,在2026年1月7日的每日一题中出现。作为二叉树类问题的进阶题型,它不仅考察基础的树遍历能力,更考验对树结构的全局把握和数学建模思维。

题目要求我们找到一种分割二叉树的方式:将整棵树分成两个子树,使得这两个子树节点值之和的乘积达到最大。具体来说,给定一棵二叉树的根节点root,你需要删除一条边,将树分成两个子树,使得它们的节点值之和的乘积最大,最终返回这个最大乘积。

举个例子,假设我们有如下二叉树:

1 / \ 2 3 / \ \ 4 5 6

删除边2-5后,得到的两棵子树和分别为11(1+2+3+4+6)和5,乘积为55;而删除边1-3则得到子树和(1+2+4+5)=12和(3+6)=9,乘积为108。显然第二种分割方式更优。

2. 解题思路与算法选择

2.1 暴力解法与复杂度分析

最直观的解法是尝试切断每一条边,然后计算分割后的子树和及其乘积。对于n个节点的二叉树,共有n-1条边(因为树有n-1条边),每次分割后计算子树和需要O(n)时间,总时间复杂度为O(n²)。这在n较大时(比如1e5级别)显然不够高效。

2.2 优化思路:预处理子树和

更聪明的做法是先进行一次完整的树遍历,预处理出每个节点为根的子树和。这样我们可以建立一个子树和的映射表,之后在计算任意分割方案时,可以在O(1)时间内获取子树和。

具体步骤:

  1. 后序遍历整棵树,计算每个节点的子树和(节点值+左右子树和)
  2. 记录整棵树的总和total_sum
  3. 对于每个可能的切割点(非根节点),其子树和为subtree_sum,则另一部分和为total_sum - subtree_sum
  4. 计算所有可能的乘积,取最大值

这种方法将时间复杂度降到了O(n)(两次遍历),空间复杂度O(n)(存储子树和)。

2.3 数学优化:何时乘积最大?

从数学角度看,当两个数a和b的和固定时,它们的乘积a×b在a=b时达到最大。因此,我们需要找到一种分割方式,使得两个子树和尽可能接近total_sum/2。

这提示我们可以:

  1. 计算total_sum后,寻找最接近total_sum/2的子树和
  2. 这样得到的乘积(total_sum - s)*s自然会最大化

3. 代码实现与细节处理

3.1 子树和计算

首先我们需要实现子树和的计算,这里采用后序遍历的方式:

def tree_sum(root): if not root: return 0 left_sum = tree_sum(root.left) right_sum = tree_sum(root.right) return root.val + left_sum + right_sum

3.2 完整解法实现

基于上述思路,完整的Python解法如下:

class Solution: def maxProduct(self, root): self.total = 0 self.best = 0 self.subtree_sums = set() # 第一次遍历计算总和 self.total = self.tree_sum(root) # 第二次遍历记录所有子树和 self.find_subtree_sums(root) # 寻找最接近total/2的子树和 target = self.total / 2 closest = min(self.subtree_sums, key=lambda x: abs(x - target)) return (closest * (self.total - closest)) % (10**9 + 7) def tree_sum(self, node): if not node: return 0 left = self.tree_sum(node.left) right = self.tree_sum(node.right) return node.val + left + right def find_subtree_sums(self, node): if not node: return 0 left = self.find_subtree_sums(node.left) right = self.find_subtree_sums(node.right) current_sum = node.val + left + right self.subtree_sums.add(current_sum) return current_sum

3.3 关键细节与优化

  1. 模数处理:题目要求结果对10^9+7取模,这是编程竞赛中常见的要求,防止大数溢出。

  2. 子树和存储:使用集合存储所有子树和,方便快速查找最接近total/2的值。

  3. 避免重复计算:通过一次遍历计算总和,另一次遍历记录所有子树和,避免多次递归带来的性能损耗。

  4. 边界条件:

    • 空树处理
    • 单节点树处理
    • 所有节点值为负数的情况(虽然题目中节点值通常为正)

4. 复杂度分析与变种思考

4.1 时间复杂度

  • 计算总和:O(n)
  • 记录子树和:O(n)
  • 查找最接近值:O(n)(集合大小为n,线性扫描) 总体时间复杂度:O(n)

4.2 空间复杂度

  • 递归栈空间:O(h),h为树高
  • 存储子树和:O(n) 总体空间复杂度:O(n)

4.3 算法变种与扩展

  1. 多叉树版本:同样的思路可以扩展到多叉树,只需调整子树和的计算方式。

  2. 多次分割:如果题目改为允许k次分割(产生k+1棵子树),问题将变得更加复杂,可能需要动态规划。

  3. 带权边分割:如果边的权重也参与计算,问题会演变为图分割问题,难度大幅增加。

  4. 在线查询:如果需要支持动态修改节点值并实时查询最大乘积,可以考虑使用更高级的数据结构如Link-Cut Tree。

5. 常见错误与调试技巧

5.1 典型错误案例

  1. 忽略模数要求:忘记对最终结果取模,导致提交失败。

  2. 整数溢出:在计算乘积时使用32位整数导致溢出(Python通常无此问题,但其他语言需要注意)。

  3. 重复计算:对每个分割点都重新计算子树和,导致超时。

  4. 错误的最优解假设:认为最大的子树和一定产生最大乘积,实际上应该找最接近total/2的值。

5.2 调试技巧

  1. 小规模测试:先用手工计算的小树验证代码正确性。

  2. 打印中间结果:在计算子树和时打印关键节点的值,验证计算是否正确。

  3. 边界测试:

    • 单节点树
    • 完全左斜/右斜树
    • 所有节点值相同的情况
  4. 性能测试:构造深度很大的树(如1e5节点),测试算法是否能在合理时间内完成。

6. 实际应用与扩展思考

6.1 实际应用场景

这类树分割问题在实际中有多种应用:

  1. 网络分割:将计算机网络分成两个部分,最小化两部分间的通信量。

  2. 数据分区:在分布式系统中,将数据分成两部分存储在不同服务器上。

  3. 图像处理:在图像分割中,将图像分成前景和背景两部分。

  4. 社交网络分析:识别社区结构,将网络分成两个紧密连接的子群体。

6.2 进阶学习方向

  1. 树的重心:学习如何找到树的重心,这与本题的最优分割点有相似之处。

  2. 图分割算法:了解Kernighan-Lin算法、谱聚类等图分割方法。

  3. 动态树结构:学习如何高效维护动态变化的树结构信息。

  4. 并行计算:思考如何将此类问题并行化处理,特别是对于大规模树结构。

7. 个人实现心得

在实际编码过程中,有几点特别值得注意:

  1. 两次遍历的优化:最初我尝试在单次遍历中同时计算总和和记录子树和,发现逻辑复杂且容易出错。分开两次遍历虽然多了一次递归,但代码更清晰。

  2. 数学直觉的应用:理解到乘积最大化发生在两部分和最接近时,这大大简化了问题,避免了不必要的计算。

  3. 集合的使用:使用集合存储子树和使得查找最接近值变得简单,比维护一个排序数组更高效(因为不需要保持顺序)。

  4. 模数处理的时机:应该在最后一步才进行模数运算,如果在中间过程就取模,可能导致比较结果不准确。

  5. 递归深度的考虑:对于极端不平衡的树,递归可能导致栈溢出。在实际工程中,可能需要改用迭代方式实现遍历。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 9:13:00

Spring AI 技术分享:用 Java 与 Spring Boot 构建大语言模型 RAG 应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 9:12:43

企业级多agent知识库系统源码拆解:Agentic RAG与Multi-Agent协作落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 9:12:31

HBase数据迁移方案全解析:Export/Import、Snapshot、CopyTable与BulkLoad实战

1. 为什么HBase数据迁移总让人头疼干过几年大数据运维的人,几乎都经历过HBase表迁移的折磨。业务要上云、集群要换机房、硬件要升级、或者单纯想把测试环境的数据同步到生产环境,这些场景都绕不开一个核心动作:把HBase里的数据从一个地方搬到…

作者头像 李华
网站建设 2026/10/15 9:12:29

WinForm与WPF技术选型指南:工业自动化实战解析

1. 项目概述作为一名在工业自动化领域摸爬滚打十年的老码农,我见证了C#上位机开发技术的多次迭代。最近在准备2026年的新项目技术选型时,发现WinForm和WPF的争论又有了新变化。这篇文章将结合最新技术动态和实战经验,帮你理清这两个框架的真实…

作者头像 李华
网站建设 2026/10/15 9:12:10

前端面试高频考点拆解:JavaScript、浏览器机制与手写代码题精讲

面试题整理这件事,我见过太多候选人走进一个误区:以为把网上流传的题库背一遍就能过关。实际上,面试官手里的题目看起来眼熟,但考察的点根本不是标准答案本身,而是你面对一个常见问题时,能不能把背后的原理…

作者头像 李华
网站建设 2026/10/15 9:12:09

Diagram Design:系统化表达的三大核心原则与落地工作流

1. “diagram-design”不是画图软件的代名词,而是一套被严重低估的系统化表达方法论很多人看到“diagram-design”第一反应是:“哦,不就是用draw.io、Excalidraw或者Visio画个流程图?”——这恰恰是我在过去八年带教三十多个跨领域…

作者头像 李华