1666. Change the Root of a Binary Tree
Problem Description
You are given a binary tree where each node has pointers to its left child, right child, and parent. The task is to restructure the tree so that a given leaf node becomes the new root of the tree.
The rerooting process works by traversing from the leaf
node up to the original root
, and for each node along this path (except the root itself), you perform these transformations:
- If the current node has a left child, move that left child to become the right child
- Make the current node's parent become its new left child
- Remove the parent's original pointer to the current node (set it to
null
)
This essentially "flips" the parent-child relationships along the path from the leaf to the root. After this process:
- The original
leaf
becomes the new root (with no parent) - The original
root
becomes a leaf node - All parent-child relationships along the path are reversed
- Nodes not on the path remain in their relative positions
For example, if you have a path: root → A → B → leaf
, after rerooting it becomes: leaf → B → A → root
, where each arrow now represents the reversed parent-child relationship.
The solution traverses from the leaf
upward to the root
, at each step:
- Saving the grandparent reference before modifying relationships
- Moving any left child to the right
- Setting the parent as the new left child
- Updating parent pointers accordingly
- Clearing the parent's original pointer to the current node
The process continues until reaching the original root, and finally returns the leaf
as the new root with its parent pointer set to None
.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: A binary tree is a special type of graph where each node has at most two children and there are no cycles. The problem involves nodes with parent-child relationships.
Is it a tree?
- Yes: The problem explicitly states we're working with a binary tree structure. We have nodes connected in a hierarchical manner with a root, and each node (except the root) has exactly one parent.
DFS
- Yes: The flowchart leads us to DFS for tree problems.
Conclusion: The flowchart suggests using a Depth-First Search (DFS) approach for this binary tree problem.
This aligns perfectly with the solution approach, which uses a DFS-like traversal pattern. The algorithm traverses from the leaf node up to the root (following parent pointers), which is essentially a depth-first traversal in reverse. At each step, it modifies the tree structure by reversing parent-child relationships. The traversal visits each node on the path exactly once, manipulating pointers to reroot the tree, which is a classic application of DFS in tree manipulation problems.
Intuition
To understand how to reroot a tree, imagine holding a tree by its root and letting gravity pull all branches downward. Now, if you grab any leaf and lift it up to become the new root, what happens? The entire path from that leaf to the old root gets "flipped" - every parent-child relationship along this path reverses.
The key insight is that we only need to modify nodes along the path from the leaf to the root. All other nodes maintain their relative positions because they're not directly affected by this "lifting" operation.
Think of it like a chain of nodes connected from leaf to root. When we make the leaf the new root, we're essentially reversing this chain:
- The leaf's parent becomes the leaf's child
- The parent's parent becomes the parent's child
- This continues all the way up to the original root
For each node we visit while traversing upward, we need to:
- Preserve any existing left child by moving it to the right (since we'll need the left spot for the parent)
- Make the current node's parent become its left child (reversing the relationship)
- Clear the parent's original pointer to avoid cycles
The traversal naturally follows a DFS pattern - we're exploring a single path from leaf to root, modifying relationships as we go. We need to carefully track three nodes at each step: the current node, its parent, and its grandparent. The grandparent reference is crucial because once we modify the parent-child relationship, we'd lose the ability to continue traversing upward.
This approach works because binary trees have a unique path between any two nodes. By following parent pointers from leaf to root and reversing each connection, we effectively "rotate" the tree to make the leaf the new root while preserving all other structural relationships.
Learn more about Tree, Depth-First Search and Binary Tree patterns.
Solution Approach
The implementation follows a straightforward iterative approach to traverse from the leaf to the root and reverse the parent-child relationships along the path.
We start by initializing two pointers:
cur
pointing to theleaf
node (which will become our new root)p
pointing to the leaf's parent
The main algorithm uses a while loop that continues until cur
reaches the original root
. In each iteration, we perform the following steps:
-
Save the grandparent reference: Before modifying any relationships, we store
gp = p.parent
. This is crucial because once we change the parent-child relationship, we'd lose access to continue traversing upward. -
Handle existing left child: If the current node has a left child, we move it to the right position with
cur.right = cur.left
. This frees up the left position for the parent node. -
Reverse the parent-child relationship:
- Set
cur.left = p
to make the parent become the left child - Update
p.parent = cur
to establish the new parent pointer
- Set
-
Clear the old connection: We need to remove the parent's original pointer to the current node to avoid cycles:
- If
p.left == cur
, setp.left = None
- If
p.right == cur
, setp.right = None
- If
-
Move up the tree: Update pointers for the next iteration:
cur = p
(move current to the parent)p = gp
(move parent to the grandparent)
After the loop completes, the original leaf has become the new root. We finalize by setting leaf.parent = None
since the root shouldn't have a parent, and return leaf
as the new root.
The algorithm visits each node on the path from leaf to root exactly once, making the time complexity O(h)
where h
is the height of the tree. The space complexity is O(1)
as we only use a constant amount of extra space for the pointers.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach. Consider this binary tree where we want to make node 7 (a leaf) the new root:
Initial tree: 1 (root) / \ 2 3 / 4 / \ 5 6 / 7 (leaf to become root)
We'll trace through each iteration as we traverse from leaf 7 up to root 1:
Iteration 1: cur = 7
, p = 6
- Save grandparent:
gp = 4
- Node 7 has no left child, so nothing to move right
- Make parent 6 the left child:
7.left = 6
- Update parent pointer:
6.parent = 7
- Clear old connection:
6.left = None
(since 6's left was 7) - Move up:
cur = 6
,p = 4
Tree after iteration 1:
7 → 6 (7 is now parent of 6)
Iteration 2: cur = 6
, p = 4
- Save grandparent:
gp = 2
- Node 6 has no left child (we just cleared it), nothing to move
- Make parent 4 the left child:
6.left = 4
- Update parent pointer:
4.parent = 6
- Clear old connection:
4.right = None
(since 4's right was 6) - Move up:
cur = 4
,p = 2
Tree structure building:
7 → 6 → 4 (with 4 still having left child 5)
Iteration 3: cur = 4
, p = 2
- Save grandparent:
gp = 1
- Node 4 has left child 5, move it right:
4.right = 5
- Make parent 2 the left child:
4.left = 2
- Update parent pointer:
2.parent = 4
- Clear old connection:
2.left = None
(since 2's left was 4) - Move up:
cur = 2
,p = 1
Iteration 4: cur = 2
, p = 1
- Save grandparent:
gp = None
(1 is the root) - Node 2 has no left child, nothing to move
- Make parent 1 the left child:
2.left = 1
- Update parent pointer:
1.parent = 2
- Clear old connection:
1.left = None
(since 1's left was 2) - Move up:
cur = 1
,p = None
Loop ends (p is None, cur is at original root)
Finally, set 7.parent = None
to make it the proper root.
Final tree structure:
7 (new root) / 6 / 4 / \ 2 5 / 1 \ 3
The path from 7 to 1 has been completely reversed, while nodes not on this path (like 3 and 5) maintain their relative positions. Node 7 is now the root, and node 1 has become a leaf.
Solution Implementation
1"""
2# Definition for a Node.
3class Node:
4 def __init__(self, val):
5 self.val = val
6 self.left = None
7 self.right = None
8 self.parent = None
9"""
10
11
12class Solution:
13 def flipBinaryTree(self, root: "Node", leaf: "Node") -> "Node":
14 """
15 Flips a binary tree to make the given leaf node the new root.
16
17 Args:
18 root: The current root of the binary tree
19 leaf: The leaf node that will become the new root
20
21 Returns:
22 The new root of the flipped tree (the original leaf node)
23 """
24 # Start from the leaf node and work our way up to the root
25 current_node = leaf
26 parent_node = current_node.parent
27
28 # Traverse from leaf to root, reversing parent-child relationships
29 while current_node != root:
30 # Store the grandparent for the next iteration
31 grandparent_node = parent_node.parent
32
33 # If current node has a left child, move it to the right
34 # (making room for the parent to become the left child)
35 if current_node.left:
36 current_node.right = current_node.left
37
38 # Make the parent become the left child of current node
39 current_node.left = parent_node
40 parent_node.parent = current_node
41
42 # Remove the connection from parent to current node
43 # (since we've reversed the relationship)
44 if parent_node.left == current_node:
45 parent_node.left = None
46 elif parent_node.right == current_node:
47 parent_node.right = None
48
49 # Move up the tree for the next iteration
50 current_node = parent_node
51 parent_node = grandparent_node
52
53 # The original leaf is now the root, so it has no parent
54 leaf.parent = None
55
56 return leaf
57
1/*
2// Definition for a Node.
3class Node {
4 public int val;
5 public Node left;
6 public Node right;
7 public Node parent;
8};
9*/
10
11class Solution {
12 public Node flipBinaryTree(Node root, Node leaf) {
13 // Start from the leaf node that will become the new root
14 Node currentNode = leaf;
15 Node parentNode = currentNode.parent;
16
17 // Traverse from leaf to root, reversing parent-child relationships
18 while (currentNode != root) {
19 // Store grandparent before modifying parent's parent pointer
20 Node grandParentNode = parentNode.parent;
21
22 // If current node has a left child, move it to the right
23 // (since the left position will be occupied by the parent)
24 if (currentNode.left != null) {
25 currentNode.right = currentNode.left;
26 }
27
28 // Make the parent become the left child of current node
29 currentNode.left = parentNode;
30
31 // Update parent's parent pointer to point to current node
32 parentNode.parent = currentNode;
33
34 // Remove the connection from parent to current node
35 // to avoid cycles in the tree structure
36 if (parentNode.left == currentNode) {
37 parentNode.left = null;
38 } else if (parentNode.right == currentNode) {
39 parentNode.right = null;
40 }
41
42 // Move up the tree for next iteration
43 currentNode = parentNode;
44 parentNode = grandParentNode;
45 }
46
47 // The leaf is now the root, so it should have no parent
48 leaf.parent = null;
49
50 // Return the new root (which was originally the leaf)
51 return leaf;
52 }
53}
54
1/*
2// Definition for a Node.
3class Node {
4public:
5 int val;
6 Node* left;
7 Node* right;
8 Node* parent;
9};
10*/
11
12class Solution {
13public:
14 Node* flipBinaryTree(Node* root, Node* leaf) {
15 // Start from the leaf node and work our way up to the root
16 Node* current = leaf;
17 Node* parent = current->parent;
18
19 // Traverse from leaf to root, reversing parent-child relationships
20 while (current != root) {
21 // Store grandparent before modifying parent's parent pointer
22 Node* grandparent = parent->parent;
23
24 // If current node has a left child, move it to the right
25 // (preparing for parent to become left child)
26 if (current->left) {
27 current->right = current->left;
28 }
29
30 // Make parent become the left child of current node
31 current->left = parent;
32
33 // Update parent's parent pointer to point to current node
34 parent->parent = current;
35
36 // Remove the connection from parent to current node
37 // to avoid cycles in the tree structure
38 if (parent->left == current) {
39 parent->left = nullptr;
40 } else if (parent->right == current) {
41 parent->right = nullptr;
42 }
43
44 // Move up the tree for next iteration
45 current = parent;
46 parent = grandparent;
47 }
48
49 // The leaf node is now the new root, so remove its parent pointer
50 leaf->parent = nullptr;
51
52 // Return the new root (original leaf node)
53 return leaf;
54 }
55};
56
1/**
2 * Definition for a Node.
3 */
4interface Node {
5 val: number;
6 left: Node | null;
7 right: Node | null;
8 parent: Node | null;
9}
10
11/**
12 * Flips a binary tree to make the given leaf node become the new root.
13 * The tree is restructured by reversing the parent-child relationships
14 * from the leaf to the original root.
15 *
16 * @param root - The original root of the binary tree
17 * @param leaf - The leaf node that will become the new root
18 * @returns The new root of the flipped tree (the original leaf node)
19 */
20function flipBinaryTree(root: Node, leaf: Node): Node {
21 // Start from the leaf node
22 let currentNode: Node = leaf;
23 let parentNode: Node | null = currentNode.parent;
24
25 // Traverse from leaf to root, reversing parent-child relationships
26 while (currentNode !== root) {
27 // Store the grandparent for the next iteration
28 const grandParentNode: Node | null = parentNode!.parent;
29
30 // If current node has a left child, move it to the right
31 if (currentNode.left !== null) {
32 currentNode.right = currentNode.left;
33 }
34
35 // Make the parent become the left child of current node
36 currentNode.left = parentNode;
37 parentNode!.parent = currentNode;
38
39 // Remove the connection from parent to current node
40 if (parentNode!.left === currentNode) {
41 parentNode!.left = null;
42 } else if (parentNode!.right === currentNode) {
43 parentNode!.right = null;
44 }
45
46 // Move up the tree for next iteration
47 currentNode = parentNode!;
48 parentNode = grandParentNode;
49 }
50
51 // The original leaf is now the root, so it has no parent
52 leaf.parent = null;
53
54 return leaf;
55}
56
Time and Space Complexity
Time Complexity: O(h)
where h
is the height of the tree (or more precisely, the distance from the leaf node to the root node).
The algorithm traverses from the given leaf node up to the root node, visiting each node along this path exactly once. In each iteration of the while loop, we perform a constant number of operations:
- Pointer reassignments (
cur.left = p
,p.parent = cur
, etc.) - Conditional checks (
if cur.left
,if p.left == cur
, etc.)
Since we visit at most h
nodes (the path from leaf to root) and perform O(1)
operations at each node, the total time complexity is O(h)
. In the worst case where the tree is skewed, h
could be O(n)
where n
is the total number of nodes in the tree.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- Three pointer variables:
cur
,p
, andgp
- No recursive calls (iterative approach)
- No additional data structures
The modifications are done in-place by rearranging the existing pointers in the tree structure. Therefore, the space complexity is O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle the Left Child Before Reassignment
One of the most common mistakes is directly assigning the parent to current_node.left
without first handling any existing left child. This would cause the original left subtree to be lost.
Incorrect approach:
# This loses the original left child! current_node.left = parent_node # Original left child is gone
Correct approach:
# First move the left child to the right (if it exists) if current_node.left: current_node.right = current_node.left # Then assign the parent as the new left child current_node.left = parent_node
2. Not Saving the Grandparent Reference Before Modifications
Another critical mistake is trying to access parent_node.parent
after modifying the parent-child relationships. Once you change parent_node.parent = current_node
, you lose access to the original grandparent.
Incorrect approach:
while current_node != root: # Modify relationships first current_node.left = parent_node parent_node.parent = current_node # This now points to current_node, not the original grandparent! grandparent = parent_node.parent # Wrong!
Correct approach:
while current_node != root: # Save grandparent BEFORE any modifications grandparent_node = parent_node.parent # Now safe to modify relationships current_node.left = parent_node parent_node.parent = current_node
3. Creating Cycles by Not Clearing Old Parent-to-Child Pointers
Failing to remove the parent's original pointer to the current node creates a cycle in the tree structure, where nodes point to each other as both parent and child.
Incorrect approach:
# After making parent the left child of current current_node.left = parent_node parent_node.parent = current_node # But parent_node still has a child pointer to current_node! # This creates a cycle: current → parent → current
Correct approach:
# Clear the old connection from parent to current if parent_node.left == current_node: parent_node.left = None elif parent_node.right == current_node: parent_node.right = None
4. Incorrect Loop Termination Condition
Using parent_node != None
as the loop condition would cause the algorithm to miss processing the root node's transformation, leaving the tree structure incomplete.
Incorrect approach:
# This stops too early, before processing the root while parent_node: # Wrong condition # Process nodes...
Correct approach:
# Continue until current_node reaches the original root while current_node != root: # Process nodes...
5. Forgetting to Clear the New Root's Parent Pointer
After the transformation, the leaf becomes the new root and should have no parent. Forgetting to set leaf.parent = None
leaves an invalid parent pointer.
Solution: Always remember to finalize with:
leaf.parent = None return leaf
How does merge sort divide the problem into subproblems?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
Binary Tree Min Depth Prereq BFS on Tree problems bfs_intro Given a binary tree find the depth of the shallowest leaf node https assets algo monster binary_tree_min_depth png Explanation We can solve this problem with either DFS or BFS With DFS we traverse the whole tree looking for leaf nodes and record and update the minimum depth as we go With BFS though since we search level by level we are guaranteed to find the shallowest leaf node
Want a Structured Path to Master System Design Too? Don’t Miss This!