Introduction to Trees
What is a Tree?
A Tree is a hierarchical data structure consisting of nodes connected by edges. Unlike linear structures (arrays, linked lists), trees organize data in a parent-child relationship, enabling efficient searching, insertion, and deletion.
A tree with n nodes has exactly n-1 edges. There is exactly one path between any two nodes, and no cycles exist.
Why Trees are used
struct Node {
int data;
Node* left;
Node* right;
};
// vs O(n) for linear search
Characteristics & Real-world Applications
✓ Advantages
- Efficient searching with O(log n) in balanced trees.
- Natural representation of hierarchical data.
- Flexible structure — easy to grow and shrink.
- Enables powerful algorithms: BST, AVL, Red-Black, Trie.
- Optimal for range queries and ordered data.
✕ Disadvantages
- Unbalanced trees degrade to O(n) performance.
- No random access — must traverse from root.
- More complex to implement than arrays or lists.
- Extra memory overhead for pointers in each node.
- Rebalancing operations add overhead.
Tree Terminology
Types of Trees
Binary Tree Basics
Node Structure
Each node in a binary tree contains data, a left pointer, and a right pointer. The left pointer references the left child; the right pointer references the right child. Null pointers indicate no child.
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
Recursive Thinking
Trees are inherently recursive. Every subtree is itself a tree. This means most tree algorithms follow a simple pattern: do something at the current node, then recurse on left, then recurse on right.
Base case: if node is null, return. Recursive case: process node, then left subtree, then right subtree.
Pointer Representation
Each node stores pointers to its children. This is the most common and flexible representation, allowing dynamic growth and arbitrary tree shapes.
Memory: O(n) for n nodes. Each node uses space for data + 2 pointers (typically 8-16 bytes overhead per node on 64-bit systems).
Array Representation
For complete binary trees, we can store nodes in an array using index arithmetic:
left_child = 2*i + 1
right_child = 2*i + 2
parent = (i-1) / 2
// Used in heaps and segment trees
Interactive Tree Visualization
Build & Explore a Binary Tree
Tree Traversals
Depth First Search (DFS)
DFS explores as far as possible along each branch before backtracking. It uses a stack (implicit via recursion or explicit).
Preorder: Root → Left → Right
Inorder: Left → Root → Right
Postorder: Left → Right → Root
Breadth First Search (BFS)
BFS explores all nodes at the present depth before moving to the next level. It uses a queue.
Level Order: Visit nodes level by level, left to right. Essential for finding shortest paths in unweighted graphs and for tree serialization.
Iterative Traversals
Using explicit stacks to avoid recursion overhead:
stack.push(root);
while (!stack.empty()) {
node = stack.top(); stack.pop();
visit(node);
if (node.right) stack.push(node.right);
if (node.left) stack.push(node.left);
}
Morris Traversal
Morris traversal achieves O(n) time and O(1) space by temporarily modifying tree pointers (threading) to create links back to ancestors, then restoring them.
while (current) {
if (!current.left) { visit(current); current = current.right; }
else { /* find predecessor, thread, traverse */ }
}
Interactive Traversal Visualization
Animated Traversal Explorer
Tree Complexity
Must visit every node exactly once. n = number of nodes.
Balanced BST: height is log n. Unbalanced: degrades to O(n).
Find correct position and insert. Same as search complexity.
Find node, handle 0/1/2 children cases. Same as search.
Call stack depth equals tree height h. Worst case O(n) for skewed.
Explicit stack/queue size. Morris achieves O(1) space.
Applications of Trees
C++ Implementation
#include <iostream> #include <queue> using namespace std; // ── Tree Node Structure ── struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; // ── Recursive Traversals ── void preorder(TreeNode* root) { if (!root) return; cout << root->val << " "; // visit root preorder(root->left); // traverse left preorder(root->right); // traverse right } void inorder(TreeNode* root) { if (!root) return; inorder(root->left); // traverse left cout << root->val << " "; // visit root inorder(root->right); // traverse right } void postorder(TreeNode* root) { if (!root) return; postorder(root->left); // traverse left postorder(root->right); // traverse right cout << root->val << " "; // visit root } // ── Level Order (BFS) ── void levelOrder(TreeNode* root) { if (!root) return; queue<TreeNode*> q; q.push(root); while (!q.empty()) { TreeNode* curr = q.front(); q.pop(); cout << curr->val << " "; if (curr->left) q.push(curr->left); if (curr->right) q.push(curr->right); } } // ── Iterative Preorder using Stack ── void iterativePreorder(TreeNode* root) { if (!root) return; stack<TreeNode*> s; s.push(root); while (!s.empty()) { TreeNode* curr = s.top(); s.pop(); cout << curr->val << " "; if (curr->right) s.push(curr->right); if (curr->left) s.push(curr->left); } } // ── Helper: Create a sample tree ── TreeNode* createSampleTree() { 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); root->right->left = new TreeNode(6); root->right->right = new TreeNode(7); return root; } int main() { TreeNode* root = createSampleTree(); cout << "Preorder: "; preorder(root); cout << endl; cout << "Inorder: "; inorder(root); cout << endl; cout << "Postorder: "; postorder(root); cout << endl; cout << "Level: "; levelOrder(root); cout << endl; return 0; }
🤖 Ask AI — Explain any Tree concept
Interview Preparation
Interactive Quiz
Test Your Tree Knowledge
Cheat Sheet
Key Takeaways
What You Learned
- A Tree is a hierarchical data structure with n nodes and n-1 edges, containing no cycles.
- Binary Trees restrict each node to at most 2 children, enabling efficient O(log n) operations when balanced.
- Tree Traversals (Preorder, Inorder, Postorder, Level Order) visit nodes in different orders for different use cases.
- Inorder traversal of a BST produces sorted output — a powerful property for ordered data.
- Recursive thinking is natural for trees: every subtree is itself a tree.
- Morris Traversal achieves O(n) time with O(1) space using threaded pointers.
- Trees power file systems, databases, compilers, AI, and countless real-world applications.
- Always consider tree balance — unbalanced trees degrade to linked-list performance (O(n)).