↓Skip to main content
  1. LeetCode/

LeetCode 1028: Recover a Tree From Preorder Traversal

·3 mins· ·
LeetCode Hard Tree Binary-Tree Dfs Stack String
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: hard First Attempt: 2026-09-12

  • Submissions: Accepted (recursion) → Accepted (stack) → Accepted (recursion, rewritten)

Approach
#

The string is “D dashes + a number”, where D is the node depth, in preorder.

For each (depth, value) you read, decide which node it hangs under: its parent is the node of depth D - 1 on the current path.

Solution 1: a stack holding the current path
#

Keep the path from the root to the current node in a stack. Then the stack length is the depth the next node should have:

  1. Read the depth depth and the value.
  2. Pop until the stack length equals depth; the top is now the parent.
  3. Attach to the left if the parent has no left child, otherwise to the right (a single child is guaranteed to be the left one).
  4. Push the new node.
class Solution:
    def recoverFromPreorder(self, traversal: str) -> Optional[TreeNode]:
        stack = []
        i, n = 0, len(traversal)
        while i < n:
            depth = 0
            while i < n and traversal[i] == "-":
                depth += 1
                i += 1

            val = 0
            while i < n and traversal[i].isdigit():
                val = val * 10 + int(traversal[i])
                i += 1

            node = TreeNode(val)
            while len(stack) > depth:
                stack.pop()
            if stack:
                if stack[-1].left:
                    stack[-1].right = node
                else:
                    stack[-1].left = node
            stack.append(node)

        return stack[0]

Solution 2: recursion with an expected depth
#

dfs(d) peeks at the next run of dashes. If it is not d, the next node does not belong here, so it returns None without consuming anything. Otherwise it reads the value, builds the node, and recurses for the left and right subtrees.

class Solution:
    def recoverFromPreorder(self, traversal: str) -> Optional[TreeNode]:
        self.i = 0
        n = len(traversal)

        def dfs(expected_depth: int) -> Optional[TreeNode]:
            j, dashes = self.i, 0
            while j < n and traversal[j] == "-":
                j += 1
                dashes += 1
            if dashes != expected_depth:
                return None                      # peek only; self.i is unchanged

            self.i = j
            val = 0
            while self.i < n and traversal[self.i].isdigit():
                val = val * 10 + int(traversal[self.i])
                self.i += 1

            node = TreeNode(val)
            node.left = dfs(expected_depth + 1)
            node.right = dfs(expected_depth + 1)
            return node

        return dfs(0)

The key is peeking with a temporary j and updating self.i only when the depth matches.

Complexity
#

Both solutions run in O(len(traversal)) time and O(h) space.

Mistakes to Avoid
#

  • Reading a single digit: values go up to 10^9, so read until a non-digit.
  • In the recursive version, moving the pointer before the depth check, which swallows characters.
  • Popping too far in the stack version: pop until the length equals the depth.

Interview Explanation
#

The string is preorder and the dash count is the depth. I keep the root-to-current path in a stack, so its length is the next node’s depth: for each node, I pop until the stack length equals its depth, the top is the parent, and the node goes left if the parent has no left child, otherwise right. Then I push it. Each character is read once, so time is linear in the string length.

Related

LeetCode 987: Vertical Order Traversal of a Binary Tree
·2 mins
LeetCode Hard Tree Binary-Tree Bfs Sorting
LeetCode 987 note: collect (col, row, val) triples, sort them, and group by column
LeetCode 2509: Cycle Length Queries in a Tree
·2 mins
LeetCode Hard Tree Binary-Tree Lca
LeetCode 2509 note: in heap numbering the parent is v // 2, so climb the larger node until both meet at the LCA
LeetCode 1377: Frog Position After T Seconds
·3 mins
LeetCode Hard Tree Dfs Bfs Graph Probability
LeetCode 1377 note: undirected edges, seen must hold nodes, and an early arrival depends on whether the frog can still move
LeetCode 726: Number of Atoms
·3 mins
LeetCode Hard Stack String Hash-Map Sorting
LeetCode 726 note: tokenize into elements, numbers, and parentheses; handle parentheses with a stack, with a default multiplier of 1
LeetCode 488: Zuma Game
·3 mins
LeetCode Hard Dynamic-Programming Memoization Breadth-First-Search String Stack
LeetCode 488 note: with at most 16 balls on the board and 5 in hand, memoize DFS on the whole (board, hand) state, inserting only before a same-colored ball or between two equal balls
LeetCode 3816: Lexicographically Smallest String After Deleting Duplicate Characters
·3 mins
LeetCode Hard Stack Monotonic-Stack Greedy String
LeetCode 3816 note: a monotonic stack that deletes a larger top while another copy exists, then trims duplicate letters off the end since a prefix is always smaller