Postorder Traversal of a Binary Tree using Iteration
Problem and examples
| Input (Tree Structure) | Expected Output | Explanation |
|---|---|---|
| [1, null, 2, null, null, 3] | [3,2,1] | Traverse leftmost node (3), then right (2), then root (1). |
| [] | [] | Tree is empty, so no traversal output. |
| [1] | [1] | Only one node exists; postorder visits it last. |
| [1,2,3,4,5,6,7] | [4,5,2,6,7,3,1] | Left subtree (4,5,2), right subtree (6,7,3), then root (1). |
Explore Postorder Traversal of a Binary Tree - Iteration
Load an example or enter your own input. Use the player controls to follow each change.
Understanding the Problem
In postorder traversal of a binary tree, the nodes are visited in the order: left subtree → right subtree → root. This is typically done using recursion, but in this solution, we want to implement it iteratively.
The challenge is that postorder is the only traversal where the root is visited after both children, which makes an iterative approach less straightforward compared to preorder or inorder traversals. We need a way to ensure that nodes are processed after their left and right subtrees have been fully traversed.
We will walk through this step-by-step using a concrete example and build up the solution using two stacks to simulate the recursive behavior.
Step-by-Step Solution with Example
Step 1: Choose an Example
Let's take this binary tree as our example:
1
/ 2 3
/ 4 5 6
Expected postorder traversal: [4, 5, 2, 6, 3, 1]
Step 2: Initialize Two Stacks
We use stack1 for traversal and stack2 to store nodes in postorder reverse format.
Push the root (1) to stack1.
Step 3: Traverse Using stack1
While stack1 is not empty:
- Pop a node from
stack1and push it tostack2. - Push the left child of the node (if it exists) to
stack1. - Push the right child (if it exists) to
stack1.
This effectively processes root-right-left order and stores them in stack2.
Step 4: Collect Postorder from stack2
Finally, pop all elements from stack2 to get them in left-right-root (postorder) order.
From the above tree, the steps will look like:
stack1:[1] → pop 1 → push tostack2- push 2, push 3 →
stack1:[2, 3] - pop 3 → push to
stack2→ push 6 →stack1:[2, 6] - pop 6 → push to
stack2 - pop 2 → push to
stack2→ push 4, 5 →stack1:[4, 5] - pop 5 → push to
stack2, pop 4 → push tostack2
Now stack2 contains: [1, 3, 6, 2, 5, 4]. Reversing gives us the postorder: [4, 5, 2, 6, 3, 1]
Edge Cases
Case 1: Empty Tree
If the root is null, simply return an empty list as there are no nodes to traverse.
Case 2: Single Node Tree
If the tree has only one node, that node is both the root and the only node. The postorder traversal will just return that node.
Case 3: Only Left or Right Subtree
For a skewed tree (e.g., [1,null,2,3]), traversal must still follow left-right-root logic. The iterative two-stack method ensures the correct order even when one side is missing.
Case 4: Full Binary Tree
In trees where every node has 0 or 2 children, the traversal will still work seamlessly, because both children are always processed before the root is added to the result.
Finally
Iterative postorder traversal can be tricky because of the requirement to process the root after both children. Using two stacks simplifies this by reversing a modified preorder traversal. This method is reliable and handles all edge cases, including skewed and full trees.
Always remember: stack2 will give you the correct postorder traversal when you reverse the root-right-left order pushed into it.
- Start with the root node of the binary tree.
- If the tree is empty, return an empty result.
- Initialize two stacks,
stack1andstack2. - Push the root node onto
stack1. - While
stack1is not empty, pop a node from it and push that node ontostack2. - If the popped node has a left child, push it onto
stack1; if it has a right child, push it ontostack1. - After processing all nodes, pop all nodes from
stack2and record their values. This produces the postorder traversal.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
TreeNode* createNode(int val) {
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = node->right = NULL;
return node;
}
int* postorderTraversal(TreeNode* root, int* returnSize) {
if (!root) {
*returnSize = 0;
return NULL;
}
TreeNode** stack1 = malloc(100 * sizeof(TreeNode*));
TreeNode** stack2 = malloc(100 * sizeof(TreeNode*));
int top1 = -1, top2 = -1;
stack1[++top1] = root;
while (top1 >= 0) {
TreeNode* node = stack1[top1--];
stack2[++top2] = node;
if (node->left) stack1[++top1] = node->left;
if (node->right) stack1[++top1] = node->right;
}
int* result = malloc(100 * sizeof(int));
int index = 0;
while (top2 >= 0) {
result[index++] = stack2[top2--]->val;
}
*returnSize = index;
free(stack1);
free(stack2);
return result;
}
int main() {
TreeNode* root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
root->left->right = createNode(5);
int returnSize;
int* res = postorderTraversal(root, &returnSize);
printf("Postorder Traversal: ");
for (int i = 0; i < returnSize; i++) {
printf("%d ", res[i]);
}
printf("\n");
free(res);
return 0;
}C++ implementation
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
if (!root) return result;
stack<TreeNode*> s1, s2;
s1.push(root);
while (!s1.empty()) {
TreeNode* node = s1.top(); s1.pop();
s2.push(node);
if (node->left) s1.push(node->left);
if (node->right) s1.push(node->right);
}
while (!s2.empty()) {
result.push_back(s2.top()->val); s2.pop();
}
return result;
}
int main() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
vector<int> res = postorderTraversal(root);
cout << "Postorder Traversal: ";
for (int val : res) cout << val << " ";
cout << endl;
return 0;
}Python implementation
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def postorderTraversal(root):
if not root:
return []
stack1, stack2 = [root], []
while stack1:
node = stack1.pop()
stack2.append(node)
if node.left:
stack1.append(node.left)
if node.right:
stack1.append(node.right)
return [node.val for node in reversed(stack2)]
# Example usage
if __name__ == '__main__':
root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3))
print(postorderTraversal(root))Java implementation
import java.util.*;
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class Postorder {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Stack<TreeNode> s1 = new Stack<>();
Stack<TreeNode> s2 = new Stack<>();
s1.push(root);
while (!s1.isEmpty()) {
TreeNode node = s1.pop();
s2.push(node);
if (node.left != null) s1.push(node.left);
if (node.right != null) s1.push(node.right);
}
while (!s2.isEmpty()) res.add(s2.pop().val);
return res;
}
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
Postorder sol = new Postorder();
System.out.println(sol.postorderTraversal(root));
}
}JavaScript implementation
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function postorderTraversal(root) {
if (!root) return [];
const stack1 = [root], stack2 = [];
while (stack1.length) {
const node = stack1.pop();
stack2.push(node);
if (node.left) stack1.push(node.left);
if (node.right) stack1.push(node.right);
}
return stack2.reverse().map(node => node.val);
}
const root = new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3));
console.log(postorderTraversal(root));Go implementation
package main
import (
"fmt"
)
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func postorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
stack1 := []*TreeNode{root}
stack2 := []*TreeNode{}
for len(stack1) > 0 {
node := stack1[len(stack1)-1]
stack1 = stack1[:len(stack1)-1]
stack2 = append(stack2, node)
if node.Left != nil {
stack1 = append(stack1, node.Left)
}
if node.Right != nil {
stack1 = append(stack1, node.Right)
}
}
result := []int{}
for i := len(stack2) - 1; i >= 0; i-- {
result = append(result, stack2[i].Val)
}
return result
}
func main() {
root := &TreeNode{1, &TreeNode{2, &TreeNode{4, nil, nil}, &TreeNode{5, nil, nil}}, &TreeNode{3, nil, nil}}
fmt.Println("Postorder Traversal:", postorderTraversal(root))
}Rust implementation
struct TreeNode {
val: i32,
left: Option<Box<TreeNode>>,
right: Option<Box<TreeNode>>,
}
impl TreeNode {
fn new(val: i32) -> Self {
TreeNode { val, left: None, right: None }
}
}
fn postorder_traversal(root: Option<Box<TreeNode>>) -> Vec<i32> {
let mut res = vec![];
let mut s1 = vec![];
let mut s2 = vec![];
if let Some(node) = root {
s1.push(node);
}
while let Some(node) = s1.pop() {
if let Some(ref left) = node.left {
s1.push(left.clone());
}
if let Some(ref right) = node.right {
s1.push(right.clone());
}
s2.push(node.val);
}
s2.reverse();
s2
}
fn main() {
let root = Some(Box::new(TreeNode {
val: 1,
left: Some(Box::new(TreeNode {
val: 2,
left: Some(Box::new(TreeNode::new(4))),
right: Some(Box::new(TreeNode::new(5)),
})),
right: Some(Box::new(TreeNode::new(3))),
}));
println!("{:?}", postorder_traversal(root));
}Kotlin implementation
class TreeNode(var `val`: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}
fun postorderTraversal(root: TreeNode?): List<Int> {
val stack1 = mutableListOf<TreeNode>()
val stack2 = mutableListOf<TreeNode>()
val result = mutableListOf<Int>()
if (root == null) return result
stack1.add(root)
while (stack1.isNotEmpty()) {
val node = stack1.removeAt(stack1.size - 1)
stack2.add(node)
node.left?.let { stack1.add(it) }
node.right?.let { stack1.add(it) }
}
while (stack2.isNotEmpty()) {
result.add(stack2.removeAt(stack2.size - 1).`val`)
}
return result
}
fun main() {
val root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left!!.left = TreeNode(4)
root.left!!.right = TreeNode(5)
println(postorderTraversal(root))
}Swift implementation
class TreeNode {
var val: Int
var left: TreeNode?
var right: TreeNode?
init(_ val: Int) {
self.val = val
self.left = nil
self.right = nil
}
}
func postorderTraversal(_ root: TreeNode?) -> [Int] {
guard let root = root else { return [] }
var stack1: [TreeNode] = [root]
var stack2: [TreeNode] = []
while !stack1.isEmpty {
let node = stack1.removeLast()
stack2.append(node)
if let left = node.left { stack1.append(left) }
if let right = node.right { stack1.append(right) }
}
return stack2.reversed().map { $0.val }
}
let root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left!.left = TreeNode(4)
root.left!.right = TreeNode(5)
print(postorderTraversal(root))TypeScript implementation
class TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
constructor(val: number, left: TreeNode | null = null, right: TreeNode | null = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function postorderTraversal(root: TreeNode | null): number[] {
if (!root) return [];
const stack1: TreeNode[] = [root];
const stack2: TreeNode[] = [];
while (stack1.length > 0) {
const node = stack1.pop()!;
stack2.push(node);
if (node.left) stack1.push(node.left);
if (node.right) stack1.push(node.right);
}
return stack2.reverse().map(node => node.val);
}
const root = new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3));
console.log(postorderTraversal(root));