Opening the reading…
Opening the reading…
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 →Postorder means left subtree, right subtree, and root last. A stack makes the root easy to process first, but it gives you the most recently pushed child first, so directly simulating left-right-root requires extra state about whether a node has already been explored. There is a cleaner rearrangement: produce root-right-left, then reverse the completed list.
To produce root-right-left with a stack, pop a node, record its value, then push its left child before its right child. The right child is on top and is processed first; its subtree is therefore completed before the left child is popped. The sequence is root-right-left, and reversing it changes the order to left-right-root without changing which nodes were visited.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | Each node is pushed, popped, recorded, and included in the reversal once. The stack operations are constant time per node, and reverse touches each recorded value once, so no node causes repeated traversal. |
| Space | O(n) extra | The output vector is O(n), but it is required output and is excluded from the extra-space bound. The working stack can contain O(n) nodes; this worst case is possible for a tree shaped so that many pending nodes accumulate, and the bound remains O(n) for every input shape. |
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
if (root == nullptr) {
return result;
}
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left != nullptr) {
st.push(node->left);
}
if (node->right != nullptr) {
st.push(node->right);
}
}
reverse(result.begin(), result.end());
return result;
}
};The two child pushes are deliberately ordered. Because a stack removes the most recently pushed item, pushing left first and right second causes the right subtree to be processed first. The final reverse is not cosmetic: it changes root-right-left into left-right-root, exactly matching the definition of postorder.
#include <vector>
using namespace std;
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
walk(root, result);
return result;
}
private:
void walk(TreeNode* node, vector<int>& result) {
if (node == nullptr) {
return;
}
walk(node->left, result);
walk(node->right, result);
result.push_back(node->val);
}
};This version is a different arrangement, not an asymptotic optimisation. Its control flow mirrors the definition exactly: finish the left subtree, finish the right subtree, then record the node. The runtime call stack stores the unfinished ancestors, while the iterative version stores pending nodes explicitly in st. Both take O(n) time and O(n) extra space in the worst case; choose recursion for clarity and the explicit stack when you want direct control over memory.