Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1666. Change the Root of a Binary Tree - Leetcode Solution

Problem Description

You are given the root of a binary tree, and a target node leaf that exists in the tree. Your task is to "change the root" of the tree to this leaf node, such that the parent-child relationships along the path from the original root to leaf are reversed. All other parts of the tree should remain unchanged.

In other words, after the operation, leaf becomes the new root, and for every node along the path from the old root to leaf, the child-parent pointers are reversed. The rest of the tree (subtrees not on this path) should remain attached to their original parents.

Constraints:

  • Each node in the binary tree has a unique value.
  • The leaf node is guaranteed to exist in the tree.
  • There is only one valid solution for the given leaf.

Thought Process

The main challenge is to reverse the parent-child pointers along the path from the original root to the given leaf. We need to be careful to not disturb the rest of the tree. Brute-forcing by reconstructing the entire tree is unnecessary and error-prone.

Instead, we should focus on:

  • Finding the path from the root to leaf.
  • Reversing the pointers along this path, so each node points to its parent as its child.
  • Ensuring that any subtrees not on this path remain attached to their original parents.
The conceptual shift is from "rebuilding" to "in-place pointer reversal" along a simple path.

Solution Approach

Let's break down the solution step-by-step:

  1. Find the Path:
    • Perform a DFS (Depth-First Search) from the root to the leaf node.
    • Store the path as a list or stack of nodes.
  2. Reverse the Pointers:
    • Iterate from leaf up to the root along the path.
    • For each node, set its child pointer (left or right) to point to its parent.
    • Break the original parent link to this node, so we don't create cycles.
    • Handle the other child (the one not on the path) by keeping it attached to the node being reversed.
  3. Return the New Root:
    • After reversing, leaf becomes the new root. Return it.

We use a stack or list to store the path for easy reversal. This approach is efficient and only modifies the necessary pointers.

Example Walkthrough

Consider the following tree:

        1
       / \
      2   3
     /
    4
  

If leaf = 4, the path is [1, 2, 4].

  1. Find path: 1 → 2 → 4.
  2. Reverse pointers:
    • 4's left and right are None. Set 4's left to 2 (its parent), 4 becomes new root.
    • 2's left was 4 (path child), so set 2's left to None, and 2's right remains as is.
    • 2's right is None, nothing to do.
    • 1's left was 2 (path child), so set 1's left to None, and 1's right (3) remains as is.
  3. Final tree:
            4
           /
          2
         /
        1
         \
          3
          

The rest of the tree (node 3) remains attached to node 1, which is now a child of 2, which is a child of 4.

Time and Space Complexity

Brute-force approach: Rebuilding the entire tree would take O(N) time and O(N) space, where N is the number of nodes.

Optimized approach (pointer reversal):

  • Time Complexity: O(H), where H is the height of the tree, since we only traverse the path from root to leaf.
  • Space Complexity: O(H), for storing the path during traversal.
This is efficient since the operation only touches the nodes on the path from root to leaf.

Summary

The key insight is to reverse the parent-child pointers along the path from the original root to the target leaf node, rather than rebuilding the whole tree. This approach is both time and space efficient, requiring only O(H) operations, and carefully preserves the rest of the tree. The solution demonstrates how understanding pointer manipulation in trees enables elegant in-place transformations.

Code Implementation

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def changeRoot(self, root: TreeNode, leaf: TreeNode) -> TreeNode:
        # Find path from root to leaf
        path = []

        def dfs(node, target):
            if not node:
                return False
            path.append(node)
            if node == target:
                return True
            if dfs(node.left, target) or dfs(node.right, target):
                return True
            path.pop()
            return False

        dfs(root, leaf)

        # Reverse the path pointers
        prev = None
        for i in range(len(path)-1, -1, -1):
            curr = path[i]
            parent = path[i-1] if i > 0 else None
            # Detach curr from its parent
            if parent:
                if parent.left == curr:
                    parent.left = None
                else:
                    parent.right = None
            # Attach previous node as left child (or right, but left is fine)
            if prev:
                # Attach prev as left or right if curr's left is None
                if curr.left is None:
                    curr.left = prev
                else:
                    curr.right = prev
            prev = curr
        return leaf
      
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* changeRoot(TreeNode* root, TreeNode* leaf) {
        vector<TreeNode*> path;
        // Find path from root to leaf
        function<bool(TreeNode*, TreeNode*)> dfs = [&](TreeNode* node, TreeNode* target) {
            if (!node) return false;
            path.push_back(node);
            if (node == target) return true;
            if (dfs(node->left, target) || dfs(node->right, target)) return true;
            path.pop_back();
            return false;
        };
        dfs(root, leaf);

        TreeNode* prev = nullptr;
        for (int i = path.size() - 1; i >= 0; --i) {
            TreeNode* curr = path[i];
            TreeNode* parent = (i > 0) ? path[i-1] : nullptr;
            // Detach curr from its parent
            if (parent) {
                if (parent->left == curr) parent->left = nullptr;
                else parent->right = nullptr;
            }
            // Attach prev as left or right child
            if (prev) {
                if (!curr->left) curr->left = prev;
                else curr->right = prev;
            }
            prev = curr;
        }
        return leaf;
    }
};
      
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
import java.util.*;
class Solution {
    public TreeNode changeRoot(TreeNode root, TreeNode leaf) {
        List<TreeNode> path = new ArrayList<>();

        // Find path from root to leaf
        boolean dfs(TreeNode node, TreeNode target, List<TreeNode> pathList) {
            if (node == null) return false;
            pathList.add(node);
            if (node == target) return true;
            if (dfs(node.left, target, pathList) || dfs(node.right, target, pathList)) return true;
            pathList.remove(pathList.size() - 1);
            return false;
        }

        dfs(root, leaf, path);

        TreeNode prev = null;
        for (int i = path.size() - 1; i >= 0; --i) {
            TreeNode curr = path.get(i);
            TreeNode parent = (i > 0) ? path.get(i-1) : null;
            // Detach curr from its parent
            if (parent != null) {
                if (parent.left == curr) parent.left = null;
                else parent.right = null;
            }
            // Attach prev as left or right child
            if (prev != null) {
                if (curr.left == null) curr.left = prev;
                else curr.right = prev;
            }
            prev = curr;
        }
        return leaf;
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} leaf
 * @return {TreeNode}
 */
var changeRoot = function(root, leaf) {
    const path = [];
    // Find path from root to leaf
    function dfs(node, target) {
        if (!node) return false;
        path.push(node);
        if (node === target) return true;
        if (dfs(node.left, target) || dfs(node.right, target)) return true;
        path.pop();
        return false;
    }
    dfs(root, leaf);

    let prev = null;
    for (let i = path.length - 1; i >= 0; --i) {
        const curr = path[i];
        const parent = (i > 0) ? path[i-1] : null;
        // Detach curr from its parent
        if (parent) {
            if (parent.left === curr) parent.left = null;
            else parent.right = null;
        }
        // Attach prev as left or right child
        if (prev) {
            if (curr.left === null) curr.left = prev;
            else curr.right = prev;
        }
        prev = curr;
    }
    return leaf;
};