951、翻转等价二叉树
我们可以为二叉树 T 定义一个 翻转操作 ,如下所示:选择任意节点,然后交换它的左子树和右子树。
只要经过一定次数的翻转操作后,能使 X 等于 Y,我们就称二叉树 X 翻转 等价 于二叉树 Y。
这些树由根节点 root1 和 root2 给出。如果两个二叉树是否是翻转 等价 的函数,则返回 true ,否则返回 false 。
示例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
思路:
对于根节点来说,翻转操作只会将左右子节点对调,对于子节点的子树并未有改动,所以考虑未翻转和翻转两种情况即可
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.right) and
self.flipEquiv(root1.right, root2.left)) or
(self.flipEquiv(root1.left, root2.left) and
self.flipEquiv(root1.right, root2.right))
971、翻转二叉树以匹配先序遍历
给你一棵二叉树的根节点 root ,树中有 n 个节点,每个节点都有一个不同于其他节点且处于 1 到 n 之间的值。
另给你一个由 n 个值组成的行程序列 voyage ,表示 预期 的二叉树 先序遍历 结果。
请翻转 最少 的树中节点,使二叉树的 先序遍历 与预期的遍历行程 voyage 相匹配 。
如果可以,则返回 翻转的 所有节点的值的列表。你可以按任何顺序返回答案。如果不能,则返回列表 [-1]。
示例1:
输入:root = [1,2], voyage = [2,1]
输出:[-1]
解释:翻转节点无法令先序遍历匹配预期行程。
示例2:
输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
解释:交换节点 2 和 3 来翻转节点 1 ,先序遍历可以匹配预期行程。
示例3:
输入:root = [1,2,3], voyage = [1,2,3]
输出:[]
解释:先序遍历已经匹配预期行程,所以不需要翻转节点。
思路:
给出一个先序遍历序列和二叉树,判断该序列是否能够由二叉树翻转而来,并找到翻转的节点。由于是先序遍历,这里很容易想到DFS先序遍历来处理。
class Solution:
def flipMatchVoyage(self, root: TreeNode, voyage: List[int]) -> List[int]:
# 定义变量,i是当前先序遍历的下标
# n是先序遍历序列的长度,避免遍历时导致下标溢出
# cur是当前根节点,用于先序遍历
# ans是结果集,这里使用集合,由于这里在先序遍历时,进行了翻转,那么在遍历右子节点时,也应该进行翻转
# stack是当前先序遍历的栈
i,n,cur,ans,stack = 0, len(voyage),root,set(),[]
while cur or stack:
while cur:
stack.append(cur)
# 如果当前结点和先序遍历序列值不一样,直接返回[-1]
if cur.val != voyage[i]:
return [-1]
i += 1
if i > n - 1: return list(ans)
# 判断是否进行翻转,如果左子节点不相等,假设进行翻转,将该父节点添加到结果集中,并指向右子树
# 否则指向左子树
if cur.left and cur.left.val != voyage[i]:
ans.add(cur.val)
cur = cur.right
else:
cur = cur.left
cur = stack.pop()
# 这里判断父节点是否在结果集中,如果在,说明之前进行翻转,这里也需要进行翻转
if cur.val in ans:
cur = cur.left
else:
cur = cur.right
# 如果树遍历完成,但是下标比n小,说明数组的长度比树的结点多,返回[-1],否则返回结果集
if i < n: return [-1]
return list(ans)
也可以使用递归来做这道题
class Solution:
def flipMatchVoyage(self, root: TreeNode, voyage: List[int]) -> List[int]:
# ans是返回的结果集
# index是当前先序遍历的下标
self.ans = []
self.index= 0
def dfs(root):
if not root: return
# 如果根节点和先序遍历值不等,说明不满足条件,那么设置结果集为[-1]
if root.val != voyage[self.index]:
self.ans = [-1]
return
self.index += 1
if self.index > len(voyage) - 1: return
# 这里判断是否需要进行翻转,如果翻转即先遍历右子树,再左子树,否则相反
if root.left and root.left.val != voyage[self.index]:
self.ans.append(root.val)
dfs(root.right)
dfs(root.left)
else:
dfs(root.left)
dfs(root.right)
dfs(root)
# 由于递归不能够马上返回,这里可能出现结果集为[-1]之后,后来又将其他的翻转结点添加了进来
if self.ans and self.ans[0] == -1: return [-1]
return self.ans