【LeeCode】971.翻转二叉树以匹配先序遍历
题目
给定一个有 N
个节点的二叉树,每个节点都有一个不同于其他节点且处于 {1, ..., N}
中的值。
通过交换节点的左子节点和右子节点,可以翻转该二叉树中的节点。
考虑从根节点开始的先序遍历报告的 N
值序列。将这一 N
值序列称为树的行程。
(回想一下,节点的先序遍历意味着我们报告当前节点的值,然后先序遍历左子节点,再先序遍历右子节点。)
我们的目标是翻转最少的树中节点,以便树的行程与给定的行程 voyage
相匹配。
如果可以,则返回翻转的所有节点的值的列表。你可以按任何顺序返回答案。
如果不能,则返回列表 [-1]
。
示例 1:
输入:root = [1,2], voyage = [2,1]
输出:[-1]
示例 2:
输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
示例 3:
输入:root = [1,2,3], voyage = [1,2,3]
输出:[]
提示:1 <= N <= 100
解题思路
模拟法
顾名思义,模拟每一次的翻转。
1.如果不需要翻转就是正确的描述则分别前序遍历。
2.如果需要翻转,则执行一次翻转后再继续分别前序遍历。
这种方法比较直观,每次翻转可能需要一定的时间。但是因为一些原因我写的没法过题,所以只是放一下思路。
时间复杂度:O(n)
,空间复杂度:O(1)
。
动态遍历法
由于模拟法写崩了,而且有了目前这种写法比较简单的方法,所以我换了一种思路,即按照需要达成的结尾来走。以下是基本思路:
1.如果目前的节点treeNode.val
和voyage
中的数据不同,则代表无法达成目标,返回-1
。
2.如果下一个节点在左节点中,则往左走,否则往右走,随后走另外一边。
3.如果往右走时,左节点是存在的,那么需要记录一次翻转。
时间复杂度:O(n)
,空间复杂度:O(1)
。
class Solution {
List<Integer> ans = new ArrayList<>();
int node = -1;
public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
if (canRunInTreeNodeByVoyage(root, voyage)) return ans;
List<Integer> wrong = new ArrayList<>();
wrong.add(-1);
return wrong;
}
public boolean canRunInTreeNodeByVoyage(TreeNode treeNode, int[] voyage) {
node++;
if (node >= voyage.length) return true;
if (treeNode.val != voyage[node]) return false;
if (treeNode.left == null && treeNode.right == null) return true;
boolean left = true, right = true;
if (treeNode.left != null && treeNode.left.val == voyage[node + 1]) {
// 左边是正确的下一个节点
left = canRunInTreeNodeByVoyage(treeNode.left, voyage);
if (treeNode.right != null) right = canRunInTreeNodeByVoyage(treeNode.right, voyage);
return left && right;
}
if (treeNode.right != null && treeNode.right.val == voyage[node + 1]) {
// 右边是正确的下一个节点
right = canRunInTreeNodeByVoyage(treeNode.right, voyage);
if (treeNode.left != null) {
left = canRunInTreeNodeByVoyage(treeNode.left, voyage);
ans.add(treeNode.val);
}
return left && right;
}
return false; // 两边都没法走,就是错的
}
}