Your Progress
67% complete. Continue learning to master DSA!
Tree Traversal Algorithms
Tree traversal is the process of visiting each node in a tree data structure exactly once. Different traversal methods can visit nodes in different orders, each suited for specific applications.
Key Takeaways
- Depth-First Traversals include pre-order (root-left-right), in-order (left-root-right), and post-order (left-right-root)
- Breadth-First Traversal visits nodes level-by-level, from left to right
- Tree traversals can be implemented recursively or iteratively
- In-order traversal of a binary search tree yields nodes in sorted order
- Time complexity for all traversal methods is O(n) where n is the number of nodes
Understanding Tree Traversal
Tree traversal is a fundamental operation in tree data structures that involves systematically visiting every node in the tree. Unlike linear data structures like arrays or linked lists that have a natural sequential order, trees can be traversed in multiple ways.
Tree Traversal Categories
Depth-First Traversal (DFT)
Explores as far as possible along each branch before backtracking.
- Pre-order (Root, Left, Right)
- In-order (Left, Root, Right)
- Post-order (Left, Right, Root)
Breadth-First Traversal (BFT)
Explores all nodes at the present depth before moving to nodes at the next depth level.
- Level-order traversal
Example Binary Tree
We'll use this binary tree for our traversal examples:
1 / \ 2 3 / \ / \ 4 5 6 7
This binary tree has 7 nodes with node 1 as the root. We'll demonstrate different traversal methods on this tree.
Depth-First Traversal Methods
1. Pre-order Traversal (Root-Left-Right)
In pre-order traversal, we visit the root node first, then traverse the left subtree, and finally the right subtree.
Order for our example tree:
1 → 2 → 4 → 5 → 3 → 6 → 7
Recursive Implementation:
function preOrderTraversal(root) { if (root === null) return; // Visit the root node console.log(root.value); // Traverse left subtree preOrderTraversal(root.left); // Traverse right subtree preOrderTraversal(root.right); } // Example usage preOrderTraversal(rootNode); // Outputs: 1, 2, 4, 5, 3, 6, 7
Iterative Implementation:
function preOrderIterative(root) { if (root === null) return; const stack = [root]; while (stack.length > 0) { // Pop a node from the stack const node = stack.pop(); // Print the node's value console.log(node.value); // Push right child first so that left child is processed first if (node.right) stack.push(node.right); if (node.left) stack.push(node.left); } } // Example usage preOrderIterative(rootNode); // Outputs: 1, 2, 4, 5, 3, 6, 7
Applications:
- Creating a copy of the tree
- Getting prefix expression of an expression tree
- Serializing and deserializing a binary tree
2. In-order Traversal (Left-Root-Right)
In in-order traversal, we first traverse the left subtree, then visit the root node, and finally traverse the right subtree.
Order for our example tree:
4 → 2 → 5 → 1 → 6 → 3 → 7
Recursive Implementation:
function inOrderTraversal(root) { if (root === null) return; // Traverse left subtree inOrderTraversal(root.left); // Visit the root node console.log(root.value); // Traverse right subtree inOrderTraversal(root.right); } // Example usage inOrderTraversal(rootNode); // Outputs: 4, 2, 5, 1, 6, 3, 7
Iterative Implementation:
function inOrderIterative(root) { if (root === null) return; const stack = []; let current = root; while (current !== null || stack.length > 0) { // Reach the leftmost node while (current !== null) { stack.push(current); current = current.left; } // Current is now null, pop an element from the stack current = stack.pop(); // Visit the node console.log(current.value); // Move to the right subtree current = current.right; } } // Example usage inOrderIterative(rootNode); // Outputs: 4, 2, 5, 1, 6, 3, 7
Applications:
- Getting values in sorted order in a binary search tree
- Getting infix expression from an expression tree
3. Post-order Traversal (Left-Right-Root)
In post-order traversal, we first traverse the left subtree, then the right subtree, and finally visit the root node.
Order for our example tree:
4 → 5 → 2 → 6 → 7 → 3 → 1
Recursive Implementation:
function postOrderTraversal(root) { if (root === null) return; // Traverse left subtree postOrderTraversal(root.left); // Traverse right subtree postOrderTraversal(root.right); // Visit the root node console.log(root.value); } // Example usage postOrderTraversal(rootNode); // Outputs: 4, 5, 2, 6, 7, 3, 1
Iterative Implementation:
function postOrderIterative(root) { if (root === null) return; const stack1 = [root]; const stack2 = []; // First stack is to help us build the second stack 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); } // Second stack now has nodes in post-order while (stack2.length > 0) { const node = stack2.pop(); console.log(node.value); } } // Example usage postOrderIterative(rootNode); // Outputs: 4, 5, 2, 6, 7, 3, 1
Applications:
- Deleting the tree (nodes must be deleted bottom-up)
- Getting postfix expression of an expression tree
- Finding the height of a tree
Breadth-First Traversal: Level Order
Level order traversal visits nodes level by level, from left to right. It uses a queue data structure instead of recursion or a stack.
Level Order Traversal
Order for our example tree:
1 → 2 → 3 → 4 → 5 → 6 → 7
Implementation:
function levelOrderTraversal(root) { if (root === null) return; const queue = [root]; while (queue.length > 0) { // Remove the front of the queue const node = queue.shift(); // Visit the node console.log(node.value); // Add the node's children to the queue if (node.left) queue.push(node.left); if (node.right) queue.push(node.right); } } // Example usage levelOrderTraversal(rootNode); // Outputs: 1, 2, 3, 4, 5, 6, 7
Printing Levels Separately:
function printLevelOrder(root) { if (root === null) return; const queue = [root]; while (queue.length > 0) { const levelSize = queue.length; const currentLevel = []; for (let i = 0; i < levelSize; i++) { const node = queue.shift(); currentLevel.push(node.value); if (node.left) queue.push(node.left); if (node.right) queue.push(node.right); } console.log(currentLevel.join(' ')); } } // Example usage printLevelOrder(rootNode); // Outputs: // 1 // 2 3 // 4 5 6 7
Applications:
- Level-by-level processing
- Finding the minimum depth of a binary tree
- Connect nodes at the same level in a binary tree
- Zigzag traversal of a binary tree
Time and Space Complexity
Traversal Method | Time Complexity | Space Complexity |
---|---|---|
Pre-order (Recursive) | O(n) | O(h) - height of tree |
Pre-order (Iterative) | O(n) | O(h) - height of tree |
In-order (Recursive) | O(n) | O(h) - height of tree |
In-order (Iterative) | O(n) | O(h) - height of tree |
Post-order (Recursive) | O(n) | O(h) - height of tree |
Post-order (Iterative) | O(n) | O(n) - worst case |
Level Order | O(n) | O(w) - max width of tree |
In the worst case, the space complexity for recursive implementations is O(n) for a skewed tree (where h = n). Similarly, for level order traversal, the space complexity could be O(n) for a complete binary tree in the last level.
Advanced Tree Traversal Techniques
Morris Traversal
A way to traverse binary trees using O(1) space by temporarily modifying the tree structure.
Key Benefits:
- No recursion or stack required
- O(1) space complexity
- Useful when stack space is limited
Zigzag Traversal
A modified level order traversal where levels are processed in alternating directions.
Example Output:
For our example tree:
Level 0: 1
Level 1: 3, 2 (right to left)
Level 2: 4, 5, 6, 7 (left to right)
Boundary Traversal
Visits all the boundary nodes of a binary tree in counter-clockwise direction.
Process:
- Print left boundary (top-down)
- Print leaves (left to right)
- Print right boundary (bottom-up)
Diagonal Traversal
Visits nodes that are on the same diagonal line from top to bottom.
Implementation Hint:
Use a queue or a hash map to group nodes by their diagonal distance.
Common Tree Traversal Problems
1. Construct a Binary Tree from Traversals
A binary tree can be constructed from certain combinations of traversals:
- From In-order and Pre-order traversals
- From In-order and Post-order traversals
- Cannot be constructed from just Pre-order and Post-order (without additional info)
2. Tree Serialization and Deserialization
Convert a tree to a string (serialization) and back (deserialization) using traversal techniques.
3. Check if Two Trees are Identical
function areIdentical(root1, root2) { // Both empty if (root1 === null && root2 === null) return true; // Both non-empty, compare them if (root1 !== null && root2 !== null) { return ( root1.value === root2.value && areIdentical(root1.left, root2.left) && areIdentical(root1.right, root2.right) ); } // One empty, one not return false; }
4. Finding the Diameter of a Binary Tree
The diameter (or width) of a tree is the number of nodes on the longest path between any two leaf nodes.
Conclusion
Tree traversal algorithms are fundamental to many tree-based operations and problem-solving techniques. Understanding the different traversal methods and when to apply them is essential for efficient manipulation of tree data structures. Whether you're dealing with binary search trees, expression trees, or other specialized tree structures, choosing the right traversal method can significantly impact the elegance and efficiency of your solution.
Remember
When choosing a traversal method, consider:
- The order in which you need to process nodes
- The specific requirements of your problem
- Whether recursive or iterative implementation is more appropriate
- The space constraints of your application
Next Steps
To further your understanding of trees and related algorithms, explore these topics:
Related Tutorials
Related Tutorials
Binary Search Trees
Learn about binary search trees, which rely heavily on traversal algorithms.
Learn moreDepth-First Search
Explore DFS, which is the foundation for several tree traversal methods.
Learn moreBreadth-First Search
Learn about BFS, which is used in level-order tree traversal.
Learn more