1. 问题背景与核心挑战
今天要讨论的是LeetCode第971题"翻转二叉树以匹配先序遍历"。这是一道关于二叉树操作的经典题目,主要考察对二叉树遍历和节点操作的理解。题目要求我们通过翻转某些节点的左右子树,使得二叉树的先序遍历结果与给定的目标序列匹配。
在实际面试中,这类二叉树操作问题经常出现。根据我的经验,亚马逊、微软等公司在技术面试中特别喜欢考察二叉树相关的题目。这道题之所以被标记为"耗时100",说明它在实际解题过程中存在一定难度,需要特别注意时间复杂度的控制。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目详细解析
2.1 题目要求理解
题目给出一个二叉树的根节点和一个表示先序遍历结果的整数数组。我们需要确定是否可以通过翻转某些节点的左右子树(可以翻转零次或多次),使得二叉树的先序遍历结果与给定的数组匹配。如果可以,返回需要翻转的节点值的列表;否则返回包含-1的列表。
举个例子:
code复制输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
解释:交换节点1的左右子树后,先序遍历结果为[1,3,2],与voyage匹配。
2.2 解题思路分析
解决这个问题的关键在于理解二叉树先序遍历的特性以及翻转操作对遍历结果的影响。我的解题思路如下:
- 先序遍历的顺序是:根节点→左子树→右子树
- 翻转一个节点会交换其左右子树的访问顺序
- 我们需要在遍历过程中实时比较当前节点值与voyage中的期望值
- 当发现不匹配时,考虑是否可以通过翻转当前节点的子树来匹配
3. 算法设计与实现
3.1 递归解法实现
递归是最直观的解决方法。我们可以按照以下步骤实现:
python复制def flipMatchVoyage(self, root, voyage):
self.index = 0
res = []
def dfs(node):
if not node:
return True
if node.val != voyage[self.index]:
return False
self.index += 1
if node.left and node.left.val != voyage[self.index]:
# 需要翻转
res.append(node.val)
node.left, node.right = node.right, node.left
return dfs(node.left) and dfs(node.right)
return res if dfs(root) else [-1]
这个解法的时间复杂度是O(N),空间复杂度是O(H),其中H是树的高度。
3.2 迭代解法实现
对于不喜欢递归的开发者,也可以使用迭代的方法:
python复制def flipMatchVoyage(self, root, voyage):
stack = [root]
res = []
i = 0
while stack:
node = stack.pop()
if not node:
continue
if node.val != voyage[i]:
return [-1]
i += 1
if node.left and node.left.val != voyage[i]:
res.append(node.val)
stack.append(node.left)
stack.append(node.right)
else:
stack.append(node.right)
stack.append(node.left)
return res
迭代解法的时空复杂度与递归解法相同,但避免了递归带来的栈溢出风险。
4. 关键点与注意事项
4.1 边界条件处理
在实际编码中,有几个边界条件需要特别注意:
- 空树的情况:如果root为None,voyage也应为空数组
- 节点值与voyage不匹配时的及时返回
- 翻转操作后仍需检查子树是否匹配
4.2 性能优化技巧
为了提升代码效率,可以考虑以下优化:
- 使用全局变量或类变量来跟踪当前voyage的索引,避免参数传递
- 提前终止:一旦发现不匹配立即返回,不再继续遍历
- 在翻转判断时,只需检查左子节点是否匹配下一个voyage值
5. 常见错误与调试技巧
5.1 典型错误案例
根据我的经验,初学者常犯的错误包括:
- 忘记在翻转后交换左右子树的引用
- 没有正确处理voyage索引的递增
- 在发现不匹配时没有立即返回
5.2 调试建议
当你的代码不能通过所有测试用例时,可以:
- 打印遍历过程中的节点值和voyage值,观察在哪里开始不匹配
- 使用小规模的测试用例手动模拟算法执行过程
- 检查翻转操作是否真的改变了遍历顺序
6. 复杂度分析与扩展思考
6.1 时间复杂度分析
两种解法的时间复杂度都是O(N),因为每个节点只被访问一次。空间复杂度取决于树的高度,最坏情况下(链表形式的树)是O(N),平均情况下是O(logN)。
6.2 类似题目推荐
如果你对这类题目感兴趣,可以尝试以下LeetCode题目:
-
- 二叉树的前序遍历
-
- 验证二叉搜索树
-
- 从前序与中序遍历序列构造二叉树
-
- 恢复二叉搜索树
7. 个人解题心得
在实际解决这个问题时,我最初尝试了复杂的条件判断,导致代码冗长且容易出错。后来发现关键在于:
- 理解翻转操作只影响当前节点的子树遍历顺序
- 只需要在左子节点不匹配时考虑翻转
- 及时处理不匹配的情况可以简化逻辑
这道题教会了我如何在二叉树问题中合理使用递归,以及在什么情况下需要考虑改变树的结构来满足特定条件。
