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:
[1, 1000]
.1 <= Node.val <= 109
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:
st
to store TreeNode
pointers.S
.depth
of the current node.TreeNode
with the extracted value.depth
, pop nodes from the stack. This ensures that we correctly place the new node as a child of the appropriate parent.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.