Medium
题目描述
对于二叉树 T,我们可以定义一个翻转操作:选择任意节点,然后交换它的左右子树。
当我们经过若干次翻转操作后,能使 X 等于 Y 时,我们称二叉树 X 翻转等价于二叉树 Y。
给你两个二叉树的根节点 root1 和 root2,判断这两个树是否翻转等价。
示例 1:
输入:root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7]
输出:true
解释:我们翻转值为 1,3 以及 5 的节点。
示例 2:
输入:root1 = [], root2 = []
输出:true
示例 3:
输入:root1 = [], root2 = [1]
输出:false
提示:
- 每棵树的节点数目范围是 [0, 100]
- 每棵树中的节点值都是唯一的、在 [0, 99] 范围内的整数
解题思路
这是一个经典的递归问题。对于两个节点,如果它们翻转等价,需要满足以下条件:
基本思路:
- 如果两个节点都为空,返回 true
- 如果只有一个节点为空,返回 false
- 如果两个节点值不相等,返回 false
- 如果值相等,则需要检查子树的翻转等价性
关键洞察: 对于值相等的两个节点,它们翻转等价当且仅当满足以下两种情况之一:
- 不翻转情况:左子树对左子树翻转等价,且右子树对右子树翻转等价
- 翻转情况:左子树对右子树翻转等价,且右子树对左子树翻转等价
这样我们就将问题递归分解为子问题。每个节点都有两种选择:保持原样或翻转,我们只需要其中一种选择能使整个树翻转等价即可。
算法步骤:
- 处理边界情况(空节点)
- 检查当前节点值是否相等
- 递归检查两种匹配方式:直接匹配和交叉匹配
- 只要有一种方式成功就返回 true
时间复杂度为 O(min(m,n)),其中 m 和 n 分别是两棵树的节点数。
代码实现
class Solution {
public:
bool flipEquiv(TreeNode* root1, TreeNode* root2) {
// 边界情况:两个都为空
if (!root1 && !root2) return true;
// 边界情况:只有一个为空,或值不相等
if (!root1 || !root2 || root1->val != root2->val) return false;
// 递归检查两种情况:不翻转或翻转
return (flipEquiv(root1->left, root2->left) && flipEquiv(root1->right, root2->right)) ||
(flipEquiv(root1->left, root2->right) && flipEquiv(root1->right, root2->left));
}
};
class Solution:
def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool:
# 边界情况:两个都为空
if not root1 and not root2:
return True
# 边界情况:只有一个为空,或值不相等
if not root1 or not root2 or root1.val != root2.val:
return False
# 递归检查两种情况:不翻转或翻转
return (self.flipEquiv(root1.left, root2.left) and self.flipEquiv(root1.right, root2.right)) or \
(self.flipEquiv(root1.left, root2.right) and self.flipEquiv(root1.right, root2.left))
public class Solution {
public bool FlipEquiv(TreeNode root1, TreeNode root2) {
// 边界情况:两个都为空
if (root1 == null && root2 == null) return true;
// 边界情况:只有一个为空,或值不相等
if (root1 == null || root2 == null || root1.val != root2.val) return false;
// 递归检查两种情况:不翻转或翻转
return (FlipEquiv(root1.left, root2.left) && FlipEquiv(root1.right, root2.right)) ||
(FlipEquiv(root1.left, root2.right) && FlipEquiv(root1.right, root2.left));
}
}
var flipEquiv = function(root1, root2) {
// 边界情况:两个都为空
if (!root1 && !root2) return true;
// 边界情况:只有一个为空,或值不相等
if (!root1 || !root2 || root1.val !== root2.val) return false;
// 递归检查两种情况:不翻转或翻转
return (flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)) ||
(flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left));
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(min(m,n)),其中 m 和 n 分别是两棵树的节点数,最坏情况下需要遍历所有对应的节点 |
| 空间复杂度 | O(min(m,n)),递归调用栈的深度取决于树的高度,最坏情况下为树的节点数 |