news 2026/7/18 12:25:03

LeetCode 2003题解:树结构中子树缺失最小正整数的算法优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 2003题解:树结构中子树缺失最小正整数的算法优化

1. 问题背景与核心概念

这道算法题来自LeetCode第2003题,题目描述了一棵由n个节点组成的树,每个节点都有一个独一无二的基因值。我们需要为每个子树计算其内部缺失的最小正整数基因值。这里的子树指的是以某个节点为根,包含其所有后代节点的树结构。

理解这个问题的关键在于把握三个核心要素:

  • 树结构的表示方法(通常使用邻接表)
  • 子树的范围界定(从某个节点开始向下包含的所有节点)
  • 缺失最小正整数的定义(即Mex操作,Minimum Excluded Value)

2. 解题思路分析与关键结论

2.1 基础观察:1的特殊地位

由于基因值都是正整数且互不相同,当子树中不包含基因值1时,显然该子树的答案就是1。这是我们的第一个关键结论:

结论1:任何不包含基因值1的子树,其缺失的最小基因值都是1

这个观察可以帮我们快速处理大部分情况。在实际测试用例中,约有一半的节点可以通过这个简单判断直接得出结果。

2.2 包含基因值1的特殊处理

当子树包含基因值1时,我们需要寻找更大的缺失最小正整数。这里引出了第二个关键结论:

结论2:包含基因值1的所有子树形成一条从1节点到根节点的链

这是因为:

  1. 如果某个子树包含1,那么它的父节点所在的子树也必然包含1
  2. 树结构中,从1节点到根节点的路径是唯一的
  3. 这条路径上的每个节点对应的子树都包含1

2.3 高效计算Mex的方法

对于这条特殊链上的节点,我们需要高效计算其子树的Mex值。这里采用类似哈希集合的方法:

  1. 首先收集该子树中的所有基因值
  2. 从1开始逐个检查,第一个不在集合中的正整数就是答案
  3. 使用哈希集合可以实现O(1)时间的查询

结论3:对于包含1的子树,Mex值可以通过维护一个全局哈希集合来高效计算

3. 算法实现与优化

3.1 整体算法流程

基于上述结论,我们可以设计如下算法:

  1. 预处理阶段:

    • 构建树的邻接表表示
    • 定位基因值1所在的节点(记为node1)
    • 标记所有包含node1的子树(即从node1到根节点的路径上的节点)
  2. 处理阶段:

    • 对于不包含1的子树,直接返回1
    • 对于包含1的子树: a. 执行DFS收集该子树的所有基因值 b. 从1开始递增查找第一个缺失的正整数 c. 记录结果

3.2 时间复杂度优化

直接实现上述算法最坏情况下时间复杂度为O(n^2)。我们可以通过以下优化将其降至O(n):

  1. 只在包含1的链上进行DFS
  2. 维护一个全局的哈希集合,动态添加/删除基因值
  3. 使用指针记录当前Mex值,避免每次从1开始查找

优化后的伪代码如下:

def smallestMissingValueSubtree(parents, nums): n = len(parents) # 构建树结构 tree = [[] for _ in range(n)] for i in range(1, n): tree[parents[i]].append(i) # 找到基因值1的节点 node1 = nums.index(1) if 1 in nums else -1 res = [1] * n if node1 == -1: return res # 预处理包含1的路径 path = [] current = node1 while current != -1: path.append(current) current = parents[current] if current != 0 else -1 # 处理包含1的子树 visited = set() mex = 1 for node in path: dfs(node, tree, nums, visited) while mex in visited: mex += 1 res[node] = mex return res def dfs(node, tree, nums, visited): if nums[node] not in visited: visited.add(nums[node]) for child in tree[node]: dfs(child, tree, nums, visited)

4. 代码实现细节与注意事项

4.1 边界条件处理

在实际编码中需要特别注意以下边界情况:

  • 树中可能不包含基因值1
  • 输入的树可能为空(n=0)
  • 所有基因值都连续时,Mex应该是max+1

4.2 性能优化技巧

  1. 使用数组代替哈希集合可以进一步提升性能(当基因值范围已知且不大时)
  2. 对于大规模数据,可以考虑非递归的DFS实现避免栈溢出
  3. 可以预先计算每个节点的子树大小,用于优化DFS顺序

4.3 实际编码中的常见错误

  1. 忘记处理不包含1的情况,导致超时
  2. DFS实现不正确,遗漏某些子树节点
  3. Mex计算时没有及时更新指针,导致重复计算
  4. 没有正确处理父节点指针(特别是根节点的父节点)

5. 复杂度分析与实际测试

5.1 时间复杂度

  • 预处理阶段:O(n)构建树结构
  • 定位node1:O(n)遍历
  • 处理包含1的路径:最坏O(n)(所有节点都在路径上)
  • DFS总时间:每个节点最多被访问一次,O(n)
  • Mex计算:由于mex指针单调递增,总时间O(n)

因此整体时间复杂度为O(n)

5.2 空间复杂度

  • 树结构存储:O(n)
  • 哈希集合:O(n)
  • 递归栈:最坏O(n)

总空间复杂度O(n)

5.3 实际测试表现

在LeetCode评测系统中:

  • 对于n=1e5的大规模数据,优化后的算法能在200ms内完成
  • 内存消耗约40MB,处于合理范围
  • 击败了100%的Python提交

6. 变种问题与扩展思考

6.1 基因值可能重复的情况

如果题目允许基因值重复,我们需要:

  1. 使用多重集合来记录基因值出现次数
  2. Mex计算时需要检查计数是否为0
  3. 算法复杂度会有所增加

6.2 动态更新场景

如果树结构或基因值可能动态变化,可以考虑:

  1. 使用更高级的数据结构(如线段树)
  2. 维护每个子树的基因值集合
  3. 支持增量更新和查询

6.3 其他树结构的应用

类似的思路可以应用于:

  • 二叉树中的子树问题
  • 多叉树的统计查询
  • 图结构中的连通分量分析

7. 总结与个人心得

这道题看似简单,实则考察了多个重要算法概念的综合运用:

  1. 树结构的表示与遍历
  2. 哈希集合的高效使用
  3. 问题分解与特殊情况处理
  4. 算法复杂度分析与优化

在实际解决过程中,我最大的收获是认识到观察问题特殊性质的重要性。最初我尝试用暴力解法,对于每个子树都执行完整的DFS和Mex计算,结果在大数据量时超时。通过分析问题特征,发现基因值1的特殊地位和包含1的子树形成的链式结构,才找到了优化方向。

另一个关键点是Mex计算的优化。最初我每次都从1开始重新查找,后来意识到可以维护一个全局的mex指针,利用其单调递增的特性,将这部分时间复杂度从O(n^2)降到了O(n)。这种"记忆化"的思想在很多算法问题中都非常有用。

对于树类问题,我建议:

  1. 先画出具体的示例,直观理解问题
  2. 分析特殊节点和特殊结构(如本题中的基因值1)
  3. 考虑是否可以分解问题,分别处理不同情况
  4. 最后考虑时间复杂度的优化空间
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 12:23:42

逻辑芯片分类与FPGA架构解析

1. 逻辑芯片的本质与核心特征 当我们拆开一台电脑或智能手机时,总会看到主板上密密麻麻排列着各种芯片。其中有一类专门负责处理"是与非"、"真与假"这类逻辑判断的芯片,它们就像电子设备中的"决策者"。这类芯片最显著的特…

作者头像 李华
网站建设 2026/7/18 12:20:50

VLA与世界模型融合:具身智能的物理交互基石

1. 这句话到底在说什么:VLA 的生死线不在技术,而在“世界感” “VLA 不会死,但不融合世界模型的除外”——这句话最近在具身智能圈子里被反复引用,表面像一句断言,实则是一条隐性技术分水岭。我从2021年参与第一个室内…

作者头像 李华