BINARY TREE › TRAVERSALS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A node belongs in all three traversals, but not at the same time. Preorder records it before its left subtree, inorder records it after the left subtree and before the right subtree, and postorder records it after both subtrees. The iterative solution only needs to remember which of these three moments is next for each node.
Put the root on a stack with state 1. State 1 means the node has just been encountered, so record preorder and move into its left child. When that left side is finished, the node reaches state 2: record inorder and move into its right child. After the right side is finished, state 3 records postorder and removes the node. A missing child simply means the next state is processed immediately.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | Each node's stack entry is processed exactly three times: once for each state. Every state performs constant work, and each child is pushed once, so no subtree is scanned again. |
| Space | O(n) | The output rows are excluded because they are required output. The stack has one entry for every active node; in a chain all n nodes can be active before the deepest node finishes, which is the worst shape and gives O(n) extra space. |
#include <stack>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>> allTraversals(TreeNode* root) {
vector<vector<int>> answer(3);
if (root == nullptr) {
return answer;
}
stack<pair<TreeNode*, int>> st;
st.push({root, 1});
while (!st.empty()) {
TreeNode* node = st.top().first;
int state = st.top().second;
if (state == 1) {
st.top().second = 2;
answer[0].push_back(node->val);
if (node->left != nullptr) {
st.push({node->left, 1});
}
} else if (state == 2) {
st.top().second = 3;
answer[1].push_back(node->val);
if (node->right != nullptr) {
st.push({node->right, 1});
}
} else {
answer[2].push_back(node->val);
st.pop();
}
}
return answer;
}
};The two state assignments are the safety mechanism of the loop. Before descending to a child, the parent is advanced so that returning to it selects the next visit moment. The child starts at state 1 and therefore follows the exact same rule. This is the iterative equivalent of pausing a recursive call between its left and right subcalls, then finishing it after the right subcall.
Recursion provides the same ordering with less explicit bookkeeping: append preorder before the left call, inorder between the two calls, and postorder after the right call. It is not an asymptotic optimisation; the runtime call stack replaces the explicit stack and still uses O(n) space in a completely one-sided tree. The iterative form is safer when a tree can be as deep as 100000 nodes.
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>> allTraversals(TreeNode* root) {
vector<vector<int>> answer(3);
walk(root, answer);
return answer;
}
private:
void walk(TreeNode* node, vector<vector<int>>& answer) {
if (node == nullptr) {
return;
}
answer[0].push_back(node->val);
walk(node->left, answer);
answer[1].push_back(node->val);
walk(node->right, answer);
answer[2].push_back(node->val);
}
};