{x}
blog image

Recover a Tree From Preorder Traversal

We run a preorder depth-first search (DFS) on the root of a binary tree.

At each node in this traversal, we output D dashes (where D is the depth of this node), then we output the value of this node.  If the depth of a node is D, the depth of its immediate child is D + 1.  The depth of the root node is 0.

If a node has only one child, that child is guaranteed to be the left child.

Given the output traversal of this traversal, recover the tree and return its root.

 

Example 1:

Input: traversal = "1-2--3--4-5--6--7"
Output: [1,2,5,3,4,6,7]

Example 2:

Input: traversal = "1-2--3---4-5--6---7"
Output: [1,2,5,3,null,6,null,4,null,7]

Example 3:

Input: traversal = "1-401--349---90--88"
Output: [1,401,null,349,88,90]

 

Constraints:

  • The number of nodes in the original tree is in the range [1, 1000].
  • 1 <= Node.val <= 109

Solution Explanation and Code

This problem involves reconstructing a binary tree from its preorder traversal, where the traversal string includes depth information. The key is to efficiently parse the traversal string and build the tree accordingly.

Approach:

We use a stack to keep track of the nodes. The stack maintains the order of the nodes as we traverse the string. The depth information (number of dashes) determines the level of each node in the tree and where it should be added as a child to a parent node already on the stack.

Algorithm:

  1. Initialization: Create an empty stack st to store TreeNode pointers.
  2. String Traversal: Iterate through the input string S.
  3. Depth Detection: Count the number of consecutive hyphens encountered. This represents the depth depth of the current node.
  4. Number Extraction: Extract the node value (integer) from the digits following the hyphens.
  5. Node Creation and Stack Management:
    • Create a new TreeNode with the extracted value.
    • While the stack size is greater than the current depth, pop nodes from the stack. This ensures that we correctly place the new node as a child of the appropriate parent.
    • If the stack is not empty, add the new node as the left child (if the parent has no left child) or right child of the top node on the stack.
    • Push the new node onto the stack.
  6. Root Retrieval: After processing the entire string, the root of the reconstructed tree will be at the bottom of the stack.

Time Complexity: O(N), where N is the length of the input string. We iterate through the string once. Stack operations (push and pop) take constant time.

Space Complexity: O(H), where H is the height of the tree. In the worst case (a skewed tree), the stack can store up to H nodes. In the average case, the space complexity is logarithmic with respect to the number of nodes.

Code (C++):

/**
 * 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* recoverFromPreorder(string traversal) {
        stack<TreeNode*> st;
        int depth = 0;
        long long num = 0; // Use long long to handle potentially large node values
 
        for (int i = 0; i < traversal.length(); ++i) {
            if (traversal[i] == '-') {
                depth++;
            } else {
                num = num * 10 + (traversal[i] - '0');
            }
 
            if (i == traversal.length() - 1 || (isdigit(traversal[i]) && traversal[i+1] == '-')) {
                TreeNode* newNode = new TreeNode(num);
                while (st.size() > depth) {
                    st.pop();
                }
                if (!st.empty()) {
                    if (st.top()->left == nullptr) {
                        st.top()->left = newNode;
                    } else {
                        st.top()->right = newNode;
                    }
                }
                st.push(newNode);
                depth = 0;
                num = 0;
            }
        }
 
        TreeNode* root = nullptr;
        while (!st.empty()) {
            root = st.top();
            st.pop();
        }
        return root;
    }
};

Note: The code uses a long long for num to handle potentially large node values, preventing integer overflow. Error handling (for invalid input formats) could be added for robustness. The TreeNode definition is assumed to be provided by LeetCode. Adapt this to your specific environment as needed.