You are given the root
of a binary tree with n
nodes, where each node is uniquely assigned a value from 1
to n
. You are also given a sequence of n
values voyage
, which is the desired pre-order traversal of the binary tree.
Any node in the binary tree can be flipped by swapping its left and right subtrees. For example, flipping node 1 will have the following effect:
Flip the smallest number of nodes so that the pre-order traversal of the tree matches voyage
.
Return a list of the values of all flipped nodes. You may return the answer in any order. If it is impossible to flip the nodes in the tree to make the pre-order traversal match voyage
, return the list [-1]
.
Example 1:
Input: root = [1,2], voyage = [2,1] Output: [-1] Explanation: It is impossible to flip the nodes such that the pre-order traversal matches voyage.
Example 2:
Input: root = [1,2,3], voyage = [1,3,2] Output: [1] Explanation: Flipping node 1 swaps nodes 2 and 3, so the pre-order traversal matches voyage.
Example 3:
Input: root = [1,2,3], voyage = [1,2,3] Output: [] Explanation: The tree's pre-order traversal already matches voyage, so no nodes need to be flipped.
Constraints:
n
.n == voyage.length
1 <= n <= 100
1 <= Node.val, voyage[i] <= n
voyage
are unique.This problem asks us to find the minimum number of node flips in a binary tree to match a given preorder traversal (voyage
). We achieve this using Depth-First Search (DFS).
Approach:
The core idea is to recursively traverse the tree, comparing the current node's value with the next expected value in the voyage
array. If they match, we proceed; otherwise, we attempt to flip the current node to see if that allows us to continue matching the voyage
.
DFS Traversal: We employ a recursive DFS function. The function takes the current node as input.
Value Check: The first thing we do is check if the current node's value (root.val
) matches the next expected value (voyage[i]
). If not, it's impossible to match the voyage
, so we set a boolean flag (ok
) to false
and return.
Left/Right Child Check: If the values match, we increment the index i
. Then we check the left child.
voyage[i]
, we recursively call dfs
on the left and then the right child.voyage[i]
, we need to flip the current node. This means we add the current node's value to our ans
array (list of flipped nodes), and then recursively call dfs
on the right child (which becomes the new "left" after the flip) and then the left child (which becomes the new "right" after the flip).Return Value: After the traversal, if ok
is still true
, we return the ans
array. Otherwise (if we encountered a mismatch and set ok
to false
), we return [-1]
, indicating that it's not possible to match the voyage
with any number of flips.
Time Complexity Analysis:
The DFS function visits each node in the tree exactly once. Therefore, the time complexity is O(N), where N is the number of nodes in the tree.
Space Complexity Analysis:
The space complexity is determined by the recursive call stack. In the worst-case scenario (a skewed tree), the depth of the recursion can be N. Therefore, the space complexity is O(N) in the worst case. In the average case (a balanced tree), it would be O(log N).
Code Examples (Python, Java, C++, Go, TypeScript):
The code examples in the problem description clearly demonstrate the implementation of the described algorithm in multiple languages. Each example follows the same logical steps outlined in the approach section. They are well-commented and easy to follow.
Example Walkthrough (Example 2):
root = [1, 2, 3]
, voyage = [1, 3, 2]
root.val
(1) matches voyage[0]
(1). i
increments to 1.voyage[1]
(3). We flip node 1. Add 1 to ans
.voyage[1]
(3).voyage[2]
(2).ans
contains [1]. The function returns [1].This detailed explanation along with the provided code examples covers the solution comprehensively.