Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1028. Recover a Tree From Preorder Traversal - Leetcode Solution

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 recoverFromPreorder(self, traversal: str) -> TreeNode:
        stack = []
        i = 0
        n = len(traversal)
        while i < n:
            depth = 0
            # Count dashes to determine depth
            while i < n and traversal[i] == '-':
                depth += 1
                i += 1
            # Read the node value
            val = 0
            while i < n and traversal[i].isdigit():
                val = val * 10 + int(traversal[i])
                i += 1
            node = TreeNode(val)
            # If stack length is greater than depth, pop to parent
            while len(stack) > depth:
                stack.pop()
            # Attach to parent if exists
            if stack:
                if not stack[-1].left:
                    stack[-1].left = node
                else:
                    stack[-1].right = node
            stack.append(node)
        return stack[0]
      
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* recoverFromPreorder(string traversal) {
        vector<TreeNode*> stack;
        int i = 0, n = traversal.size();
        while (i < n) {
            int depth = 0;
            while (i < n && traversal[i] == '-') {
                depth++;
                i++;
            }
            int val = 0;
            while (i < n && isdigit(traversal[i])) {
                val = val * 10 + (traversal[i] - '0');
                i++;
            }
            TreeNode* node = new TreeNode(val);
            while (stack.size() > depth) stack.pop_back();
            if (!stack.empty()) {
                if (!stack.back()->left) stack.back()->left = node;
                else stack.back()->right = node;
            }
            stack.push_back(node);
        }
        return stack[0];
    }
};
      
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode recoverFromPreorder(String traversal) {
        Deque<TreeNode> stack = new ArrayDeque<>();
        int i = 0, n = traversal.length();
        while (i < n) {
            int depth = 0;
            while (i < n && traversal.charAt(i) == '-') {
                depth++;
                i++;
            }
            int val = 0;
            while (i < n && Character.isDigit(traversal.charAt(i))) {
                val = val * 10 + (traversal.charAt(i) - '0');
                i++;
            }
            TreeNode node = new TreeNode(val);
            while (stack.size() > depth) stack.pop();
            if (!stack.isEmpty()) {
                if (stack.peek().left == null) stack.peek().left = node;
                else stack.peek().right = node;
            }
            stack.push(node);
        }
        while (stack.size() > 1) stack.pop();
        return stack.peek();
    }
}
      
/**
 * 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 {string} traversal
 * @return {TreeNode}
 */
var recoverFromPreorder = function(traversal) {
    let stack = [];
    let i = 0, n = traversal.length;
    while (i < n) {
        let depth = 0;
        while (i < n && traversal[i] === '-') {
            depth++;
            i++;
        }
        let val = 0;
        while (i < n && /\d/.test(traversal[i])) {
            val = val * 10 + Number(traversal[i]);
            i++;
        }
        let node = new TreeNode(val);
        while (stack.length > depth) stack.pop();
        if (stack.length) {
            if (!stack[stack.length - 1].left)
                stack[stack.length - 1].left = node;
            else
                stack[stack.length - 1].right = node;
        }
        stack.push(node);
    }
    return stack[0];
};
      

Problem Description

Given a string traversal representing the preorder traversal of a binary tree, recover the tree and return its root. In this representation:

  • Each node's value is an integer.
  • The depth of a node is denoted by the number of dashes '-' before its value.
  • The root node has depth 0 (no dashes).
  • If a node has only one child, it is always the left child.

There is exactly one valid binary tree that can be reconstructed from the given traversal string.

Thought Process

To solve this problem, we need to interpret the preorder traversal string and reconstruct the binary tree structure. The dashes tell us how deep in the tree a node is. For example, a node with two dashes before its value is a child of a node with one less dash.

A brute-force approach might involve parsing the string and recursively trying to attach nodes at the right depth, but this can get complicated and inefficient. Instead, we can use a stack to keep track of the current path from the root to the latest node, allowing us to efficiently determine where to attach each new node as we parse the string.

The key insight is that as we move through the string, the number of dashes tells us whether we need to go up (pop from the stack) or down (add a child) in the tree structure.

Solution Approach

We will reconstruct the tree using a stack to track the path from the root to the current node. Here's how:

  1. Initialize an empty stack and set a pointer at the beginning of the string.
  2. Iterate through the string:
    • Count the number of consecutive dashes to determine the depth of the next node.
    • Parse the digits immediately after the dashes to get the node's value.
    • Create a new tree node with this value.
    • If the stack's size is greater than the current depth, pop from the stack until it matches the depth (this walks up the tree to the correct parent).
    • If the stack is not empty, attach the new node as the left child if the parent has no left child, otherwise as the right child.
    • Push the new node onto the stack.
  3. After parsing the entire string, the bottom of the stack contains the root node.

This approach works efficiently because each node is processed exactly once, and the stack always represents the current path in the tree.

Example Walkthrough

Let's walk through an example with traversal = "1-2--3--4-5--6--7":

  • Parse "1": depth 0, value 1. Stack: [1]
  • Parse "-2": depth 1, value 2. Stack: [1,2] (2 is left of 1)
  • Parse "--3": depth 2, value 3. Stack: [1,2,3] (3 is left of 2)
  • Parse "--4": depth 2, value 4. Stack: [1,2,4] (pop 3, 4 is right of 2)
  • Parse "-5": depth 1, value 5. Stack: [1,5] (pop 4 and 2, 5 is right of 1)
  • Parse "--6": depth 2, value 6. Stack: [1,5,6] (6 is left of 5)
  • Parse "--7": depth 2, value 7. Stack: [1,5,7] (pop 6, 7 is right of 5)

The final tree is:

        1
       / \
      2   5
     / \ / \
    3  4 6  7
    

Time and Space Complexity

Brute-force approach: If you tried to recursively search for the correct parent for each node, you could end up with O(N^2) time if you traverse up the tree for every node.

Optimized stack approach (as above):

  • Time Complexity: O(N), where N is the length of the traversal string, because each character is processed once.
  • Space Complexity: O(H), where H is the height of the tree (worst case O(N) for a skewed tree), since the stack holds at most H nodes at a time.

Summary

By carefully parsing the traversal string and using a stack to keep track of the current path, we can efficiently reconstruct the binary tree in a single pass. The key insight is that the number of dashes tells us the depth, and the stack helps us quickly find the correct parent for each node. This method is both elegant and efficient, making it ideal for this problem.