Home/Data Structures & Algorithms/Trees, BSTs & Hierarchical Indexes

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
Binary trees, balanced BSTs, Morris traversal, lowest common ancestor (LCA), and hierarchical tree layouts.
Lowest Common Ancestor: Why Binary Lifting in O(log N) beats naive DFS in high-scale DAGs
When you have multiple online LCA queries on a static tree, the gold standard is Binary Lifting (used in compiler dominance frontiers, distributed network routing, and Git commit histories). 1. The Core Idea: Powers of 2 Parent Jumps Instead of storing only a node's immediate parent (which forces yoRead more
When you have multiple online LCA queries on a static tree, the gold standard is Binary Lifting (used in compiler dominance frontiers, distributed network routing, and Git commit histories).
1. The Core Idea: Powers of 2 Parent Jumps
Instead of storing only a node’s immediate parent (which forces you to step up the tree one node at a time in $O(N)$), what if every node stored its ancestor at distance $2^0, 2^1, 2^2, 2^3, dots, 2^k$?
We define a 2D table:
up[u][i] = the (2^i)-th ancestor of node u.The state transition is pure dynamic programming:
In English: ‘To jump $2^i$ steps up from $u$, first jump $2^{i-1}$ steps up to reach intermediate node $v$, and then from $v$ jump another $2^{i-1}$ steps!’ ($2^{i-1} + 2^{i-1} = 2^i$).
2. Answering an LCA Query in 2 Steps
To find the LCA of nodes
uandv:depth[u] < depth[v], swap them. Use binary powers to jumpuupwards untildepth[u] == depth[v]in $O(log N)$ steps. Ifu == v, they were on the same branch → returnu!uandvupwards together using the largest possible power of 2 such that their ancestors are still different (up[u][i] != up[v][i]). When no more jumps can be made, their immediate parent (up[u][0]) is their Lowest Common Ancestor!Clean C++20 Implementation
Complexity Breakdown
- Preprocessing Time:
- Preprocessing Memory:
- Per Query Time: Strictly
See lessO(N log N)via a single DFS pass.O(N log N)to store the jump table.O(log N). For $N = 1,000,000$, $log_2(1,000,000) pprox 20$ operations. You can evaluate 50,000 queries in a fraction of a second!How to traverse a Binary Tree in O(1) memory without recursion or stack (Morris Traversal)?
Morris Traversal is one of the most brilliant algorithms in computer science. It solves the exact constraint you're facing: how do you traverse a tree without spending any extra memory on a stack? 1. The Core Secret: Threaded Binary Trees When you are at a node and go deep into its left subtree, howRead more
Morris Traversal is one of the most brilliant algorithms in computer science. It solves the exact constraint you’re facing: how do you traverse a tree without spending any extra memory on a stack?
1. The Core Secret: Threaded Binary Trees
When you are at a node and go deep into its left subtree, how do you get back up to the node without a parent pointer or call stack? Normally, you need a stack to remember the return path.
J. H. Morris realized something clever: in every binary tree, about half of all pointers are NULL! Every leaf node has a
nullright child that is sitting there doing nothing.Morris repurposes these unused
nullpointers as temporary bridge wires (called “threads”) back to the inorder successor:null, point it back to the current node:predecessor->right = current. Then movecurrent = current->left.current, that means you have already finished visiting the left subtree! You print/recordcurrent->val, restore the pointer tonull(repairing the tree), and movecurrent = current->right!When the algorithm finishes, the tree is 100% restored to its original state. Zero memory allocated, zero permanent mutations!
Clean C++20 Morris Inorder Traversal
Complexity & Trade-offs
- Time Complexity:
- Space Complexity:
- Thread-Safety Warning: Because Morris Traversal temporarily mutates right pointers during execution, it is not safe for concurrent readers on the same tree instance. If multiple threads read the tree simultaneously, use standard recursive DFS with a large stack or an explicit thread-local queue.
See lessO(N). Even though we search for predecessors, each edge in the tree is traversed at most 3 times (once to find predecessor, once to create thread, once to remove thread).3 * (N - 1) = O(N).O(1)auxiliary space. Just two pointers (currandpred). No call stack, no heap allocations.