Given a binary tree with root node. Return the In-order,Pre-order and Post-order traversal of the binary tree.
Input : root = [1, 3, 4, 5, 2, 7, 6 ]
Output : [ [5, 3, 2, 1, 7, 4, 6] , [1, 3, 5, 2, 4, 7, 6] , [5, 2, 3, 7, 6, 4, 1] ]
Explanation : The In-order traversal is [5, 3, 2, 1, 7, 4, 6].
The Pre-order traversal is [1, 3, 5, 2, 4, 7, 6].
The Post-order traversal is [5, 2, 3, 7, 6, 4, 1].
Input : root = [1, 2, 3, null, null, null, 6 ]
Output : [ [2, 1, 3, 6] , [1, 2, 3, 6] , [2, 6, 3, 1] ]
Explanation : The In-order traversal is [2, 1, 3, 6].
The Pre-order traversal is [1, 2, 3, 6].
The Post-order traversal is [2, 6, 3, 1].
Input : root = [5, 1, 2, 8, null, 4, 5, null, 6]
A binary tree's preorder, inorder, and postorder traversals typically require three separate traversals. This can be obtained in a single pass using a stack for state management. The stack tracks the traversal state for each node. In the preorder state, the node's value is recorded, and its left child is pushed onto the stack. In the inorder state, the node's value is recorded, and its right child is pushed onto the stack. In the postorder state, the node's value is recorded, and the node is popped from the stack. This process pushes each value into separate arrays for preorder, inorder, and postorder traversals, enabling a single traversal to compute all three orders.
#include <bits/stdc++.h>
using namespace std;
// Definition for a binary tree node.
struct TreeNode {
int data;
TreeNode *left;
TreeNode *right;
TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};
class Solution {
public:
vector<vector<int>> treeTraversal(TreeNode* root) {
// Vectors to store the traversals
vector<int> pre, in, post;
// If the tree is empty, return empty traversals
if (root == nullptr) return { pre, in, post };
// Stack to maintain nodes and their traversal state
stack<pair<TreeNode*, int>> st;
// Start with the root node and state 1 (preorder)
st.push({ root, 1 });
while (!st.empty()) {
// Get the top element from the stack
auto [node, state] = st.top();
st.pop();
// Process the node based on its state
if (state == 1) {
// Preorder: Add node data
pre.push_back(node->data);
// Change state to 2 (inorder) for this node
st.push({ node, 2 });
// Push left child onto the stack for processing
if (node->left != nullptr) {
st.push({ node->left, 1 });
}
} else if (state == 2) {
// Inorder: Add node data
in.push_back(node->data);
// Change state to 3 (postorder) for this node
st.push({ node, 3 });
// Push right child onto the stack for processing
if (node->right != nullptr) {
st.push({ node->right, 1 });
}
} else {
// Postorder: Add node data
post.push_back(node->data);
}
}
// Return the traversals as a 2D vector
return { in, pre, post};
}
};
// Main function to test the tree traversal
int main() {
// Creating a sample binary tree
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);
Solution sol;
vector<vector<int>> traversals = sol.treeTraversal(root);
// Print Preorder traversal
cout << "Preorder traversal: ";
for (int val : traversals[0]) cout << val << " ";
cout << endl;
// Print Inorder traversal
cout << "Inorder traversal: ";
for (int val : traversals[1]) cout << val << " ";
cout << endl;
// Print Postorder traversal
cout << "Postorder traversal: ";
for (int val : traversals[2]) cout << val << " ";
cout << endl;
return 0;
}
import java.util.*;
/**
* Definition for a binary tree node.
*/
class TreeNode {
int data;
TreeNode left, right;
TreeNode(int val) {
this.data = val;
this.left = null;
this.right = null;
}
}
// Helper class to store a TreeNode and its traversal state
class NodeState {
TreeNode node;
int state;
NodeState(TreeNode node, int state) {
this.node = node;
this.state = state;
}
}
class Solution {
public List<List<Integer>> treeTraversal(TreeNode root) {
// Lists to store the traversals
List<Integer> pre = new ArrayList<>();
List<Integer> in = new ArrayList<>();
List<Integer> post = new ArrayList<>();
// If the tree is empty, return empty traversals
if (root == null) return Arrays.asList(pre, in, post);
// Stack to maintain nodes and their traversal state
Stack<NodeState> st = new Stack<>();
// Start with the root node and state 1 (preorder)
st.push(new NodeState(root, 1));
while (!st.isEmpty()) {
// Get the top element from the stack
NodeState top = st.pop();
TreeNode node = top.node;
int state = top.state;
// Process the node based on its state
if (state == 1) {
// Preorder: Add node data
pre.add(node.data);
// Change state to 2 (inorder) for this node
st.push(new NodeState(node, 2));
// Push left child onto the stack for processing
if (node.left != null) {
st.push(new NodeState(node.left, 1));
}
} else if (state == 2) {
// Inorder: Add node data
in.add(node.data);
// Change state to 3 (postorder) for this node
st.push(new NodeState(node, 3));
// Push right child onto the stack for processing
if (node.right != null) {
st.push(new NodeState(node.right, 1));
}
} else {
// Postorder: Add node data
post.add(node.data);
}
}
// Return the traversals as a list of lists
return Arrays.asList(in, pre, post);
}
}
class Main {
public static void main(String[] args) {
// Creating a sample binary tree
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);
Solution sol = new Solution();
List<List<Integer>> traversals = sol.treeTraversal(root);
// Print Preorder traversal
System.out.print("Preorder traversal: ");
for (int val : traversals.get(0)) System.out.print(val + " ");
System.out.println();
// Print Inorder traversal
System.out.print("Inorder traversal: ");
for (int val : traversals.get(1)) System.out.print(val + " ");
System.out.println();
// Print Postorder traversal
System.out.print("Postorder traversal: ");
for (int val : traversals.get(2)) System.out.print(val + " ");
System.out.println();
}
}
from typing import List
# Definition for a binary tree node.
class TreeNode:
def __init__(self, val: int):
self.data = val
self.left = None
self.right = None
class Solution:
def tree_traversal(self, root: TreeNode):
# Lists to store the traversals
pre, in_order, post = [], [], []
# If the tree is empty, return empty traversals
if root is None:
return [pre, in_order, post]
# Stack to maintain nodes and their traversal state
stack = [(root, 1)]
while stack:
# Get the top element from the stack
node, state = stack.pop()
# Process the node based on its state
if state == 1:
# Preorder: Add node data
pre.append(node.data)
# Change state to 2 (inorder) for this node
stack.append((node, 2))
# Push left child onto the stack for processing
if node.left:
stack.append((node.left, 1))
elif state == 2:
# Inorder: Add node data
in_order.append(node.data)
# Change state to 3 (postorder) for this node
stack.append((node, 3))
# Push right child onto the stack for processing
if node.right:
stack.append((node.right, 1))
else:
# Postorder: Add node data
post.append(node.data)
# Return the traversals as a list of lists
return [in_order, pre, post]
# Main function to test the tree traversal
if __name__ == "__main__":
# Creating a sample binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
sol = Solution()
traversals = sol.tree_traversal(root)
# Print Preorder traversal
print("Preorder traversal:", *traversals[0])
# Print Inorder traversal
print("Inorder traversal:", *traversals[1])
# Print Postorder traversal
print("Postorder traversal:", *traversals[2])
/**
* Definition for a binary tree node.
*/
class TreeNode {
constructor(val) {
this.data = val;
this.left = null;
this.right = null;
}
}
class Solution {
treeTraversal(root) {
// Arrays to store the traversals
let pre = [], inOrder = [], post = [];
// If the tree is empty, return empty traversals
if (root === null) return [pre, inOrder, post];
// Stack to maintain nodes and their traversal state
let stack = [[root, 1]];
while (stack.length > 0) {
// Get the top element from the stack
let [node, state] = stack.pop();
// Process the node based on its state
if (state === 1) {
// Preorder: Add node data
pre.push(node.data);
// Change state to 2 (inorder) for this node
stack.push([node, 2]);
// Push left child onto the stack for processing
if (node.left !== null) {
stack.push([node.left, 1]);
}
} else if (state === 2) {
// Inorder: Add node data
inOrder.push(node.data);
// Change state to 3 (postorder) for this node
stack.push([node, 3]);
// Push right child onto the stack for processing
if (node.right !== null) {
stack.push([node.right, 1]);
}
} else {
// Postorder: Add node data
post.push(node.data);
}
}
// Return the traversals as an array of arrays
return [inOrder, pre, post];
}
}
// Main function to test the tree traversal
(function() {
// Creating a sample binary tree
let 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);
let sol = new Solution();
let traversals = sol.treeTraversal(root);
// Print Preorder traversal
console.log("Preorder traversal:", traversals[0].join(" "));
// Print Inorder traversal
console.log("Inorder traversal:", traversals[1].join(" "));
// Print Postorder traversal
console.log("Postorder traversal:", traversals[2].join(" "));
})();
Time Complexity: O(3N), where N is the number of nodes, since each node is processed three times (preorder, inorder, postorder).
Space Complexity: O(4N), where N is the number of nodes, due to the stack and three traversal arrays.
Q: How does Postorder traversal differ in its usage compared to Inorder and Preorder?
A: Postorder traversal is used in applications such as deleting a tree, evaluating expressions, and dependency resolution. It processes children before their parent, making it useful for tasks requiring bottom-up computation.
Q: Can traversal be applied to non-binary trees (n-ary trees)?
A: While Inorder, Preorder, and Postorder are defined for binary trees, variations exist for n-ary trees. Preorder and Postorder generalize well, but Inorder requires redefining traversal order.
Q: How would you perform all three traversals using constant space?
A: Using Morris Traversal, Inorder and Preorder traversals can be performed in O(1) space by modifying tree pointers temporarily. Postorder traversal is more complex and requires additional modifications or iterative tricks.
Q: What modifications are needed if we also want to track node depths?
A: Maintaining a depth counter while traversing (either recursively or iteratively) allows returning node values along with their depths. A queue-based level-order traversal can also help in structured depth tracking.