Home/Data Structures & Algorithms/Page 2

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.
Master core and advanced data structures and algorithms with production-grade implementations, hardware-conscious optimizations, and real-world system designs.
How does a Fenwick Tree (Binary Indexed Tree) query and update prefix sums in O(log N) using i & (-i)?
The Fenwick Tree (invented by Peter Fenwick in 1994) is one of the most compact data structures ever devised. It gives you the full power of a dynamic segment tree in just one flat array of size N with 10 lines of code. 1. The Secret: Powers of 2 Range Decomposition Any positive integer can be uniquRead more
The Fenwick Tree (invented by Peter Fenwick in 1994) is one of the most compact data structures ever devised. It gives you the full power of a dynamic segment tree in just one flat array of size N with 10 lines of code.
1. The Secret: Powers of 2 Range Decomposition
Any positive integer can be uniquely represented as a sum of powers of 2. For example:
13 = 8 + 4 + 1.Fenwick took this idea and applied it to prefix ranges: instead of storing every single element,
tree[i]stores the sum of a contiguous range of length equal to its lowest set bit!The length of the range responsible by index
iis given by:lowbit(i) = i & (-i).i = 12 (1100_2):lowbit(12) = 4. Sotree[12]stores the sum of 4 elements: indices[9, 10, 11, 12]!i = 8 (1000_2):lowbit(8) = 8. Sotree[8]stores the sum of the first 8 elements:[1 ... 8]!2. The Two Operations
A. Prefix Sum Query: `i -= (i & -i)`
To calculate the prefix sum up to index
13:tree[13](covers index 13).13 - lowbit(13) = 13 - 1 = 12.tree[12](covers indices 9 through 12).12 - lowbit(12) = 12 - 4 = 8.tree[8](covers indices 1 through 8).8 - lowbit(8) = 8 - 8 = 0(Done!).Total reads: only 3 steps to sum 13 numbers! At each step, you strip off one binary bit, taking at most
O(log N)operations.B. Point Update: `i += (i & -i)`
When you add delta to element
i, which parent ranges need to be updated? Every index whose range coversi! You navigate up the tree simply by adding the lowest set bit:i += (i & -i)!Clean C++20 Implementation
Why Fenwick Beats Segment Trees in Production
Topological Sort: Kahn’s Algorithm (BFS) vs Tarjan’s DFS in massive dependency graphs
In software build graphs and task schedulers, Kahn's Algorithm (Indegree BFS) is universally favored over recursive DFS for two huge reasons: No Recursion / Call-Stack Exhaustion: DFS recursion on a graph with 500,000 chained dependencies will instantly crash with a stack overflow (RecursionError orRead more
In software build graphs and task schedulers, Kahn’s Algorithm (Indegree BFS) is universally favored over recursive DFS for two huge reasons:
RecursionErroror OS segfault). Kahn’s algorithm runs iteratively using a queue in heap memory.Unvisited,Visiting,Visited). With Kahn’s algorithm, cycle detection is automatic: if the number of sorted nodes is less than total nodes, a cycle exists!The Plain English Mental Model of Kahn’s Algorithm
Think about taking university courses. A course with
indegree = 0has zero prerequisites—you can enroll in it on Day 1!indegree(number of incoming dependency arrows) for every single node.indegree == 0and push them into a queue (these tasks can run immediately).uand add it to your execution plan.vthat depended onu, decrement its indegree (indegree[v]--).indegree[v] == 0, all its prerequisites are now satisfied! Push it into the queue!If there was a circular dependency (e.g.,
A depends on B and B depends on A), their indegrees will never reach 0, so they will never enter the queue!Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(V + E). We touch every vertex and edge exactly once.O(V + E)to store the adjacency list and indegree table.Floyd’s Tortoise and Hare: Mathematical proof of why meeting point resolves cycle origin
Floyd's cycle algorithm is pure mathematical poetry. Let's write out the distances with simple algebra so the proof is crystal clear. 1. Defining the Variables Let's map out the linked list into three distinct segments: L: Distance from the head to the cycle entrance. C: Total length (circumference)Read more
Floyd’s cycle algorithm is pure mathematical poetry. Let’s write out the distances with simple algebra so the proof is crystal clear.
1. Defining the Variables
Let’s map out the linked list into three distinct segments:
L: Distance from theheadto the cycle entrance.C: Total length (circumference) of the cycle.x: Distance from the cycle entrance to the meeting point inside the cycle.2. Distance Traveled by Each Pointer
When the Tortoise (slow) and Hare (fast) meet:
Dist_slow = L + xDist_fast = L + n * C + x(wherenis how many full laps fast ran around the cycle).Because the fast pointer moves at twice the speed of slow:
Now subtract
L + xfrom both sides:3. What does L = (n – 1) * C + (C – x) mean?
Look carefully at that equation:
Lis the distance from head to the cycle entrance.(C - x)is the distance from the meeting point to the cycle entrance!(n - 1) * Cis just zero or more full loops around the cycle!Conclusion: If you place Pointer 1 at
head(which must travel distanceL) and Pointer 2 atmeeting_point(which travels distance(C - x)plus some optional full laps), both pointers will meet at the EXACT same node: the cycle entrance!Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N). Phase 1 takes at most $2N$ steps. Phase 2 takes at most $N$ steps.O(1). No hash set or memory allocation.How does the Dutch National Flag 3-way partition work in a single pass with zero branch mispredictions?
The Dutch National Flag algorithm (invented by Edsger W. Dijkstra) is the secret weapon that makes 3-way QuickSort resilient against duplicate keys. 1. The 3 Pointer Invariant We divide the array into 4 distinct regions using 3 pointers: low, mid, and high: [ 0 ... low-1 ] -> All elements strictlRead more
The Dutch National Flag algorithm (invented by Edsger W. Dijkstra) is the secret weapon that makes 3-way QuickSort resilient against duplicate keys.
1. The 3 Pointer Invariant
We divide the array into 4 distinct regions using 3 pointers:
low,mid, andhigh:Initially,
low = 0,mid = 0, andhigh = n - 1. The entire array is initially inside theUNKNOWNregion.2. The 3 State Transitions
While
mid <= high, inspectnums[mid]:nums[mid] == 0: Swapnums[low]withnums[mid]. Increment BOTHlow++andmid++.Why can we increment
midhere? Because whatever was sitting atlowwas already processed (it was guaranteed to be a1).nums[mid] == 1: It’s already in the right spot! Just incrementmid++.nums[mid] == 2: Swapnums[mid]withnums[high]. Decrementhigh--.THE CRITICAL CATCH: Do NOT increment
midhere! Whatever came fromhighwas unknown—it might be a0, a1, or another2! We must inspect it on the next loop iteration!Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N). In every single step, eithermidincreases orhighdecreases. The unknown window(high - mid)strictly shrinks to zero in at mostNsteps.O(1). No extra memory allocated.Traveling Salesperson Problem: How does Bitmask DP reduce (N – 1)! factorial to O(N^2 * 2^N)?
The Held-Karp Bitmask DP algorithm is the premier textbook demonstration of converting a factorial combinatorial explosion into a manageable exponential dynamic programming state space. 1. The Core Insight (Subproblem Overlap) Suppose a drone visits cities in the order: 1 → 2 → 3 → 4.Read more
The Held-Karp Bitmask DP algorithm is the premier textbook demonstration of converting a factorial combinatorial explosion into a manageable exponential dynamic programming state space.
1. The Core Insight (Subproblem Overlap)
Suppose a drone visits cities in the order:
1 → 2 → 3 → 4.Another candidate path visits cities in the order:
1 → 3 → 2 → 4.Notice that in both cases, the set of visited cities is identical ({1, 2, 3, 4}), and the current ending city is identical (City 4)!
For future route choices (visiting the remaining cities 5 through 20), it does not matter how you traveled between 1, 2, and 3—all that matters is what is the minimum cost to have visited that exact subset and currently be sitting at City 4!
2. The State Definition
We define our DP state with two parameters:
dp(mask, u)mask: An integer whose binary bits represent the subset of visited cities. If bitiis1, Cityihas been visited. If bitiis0, Cityiis unvisited.u: The current city where the drone is currently parked.Transition:
To move to an unvisited city
v(where(mask & (1 << v)) == 0):Clean Python 3.12 Implementation with Memoization
Complexity Breakdown: From 10^17 down to 10^7
For $N = 20$:
- Brute force $19! pprox 1.21 imes 10^{17}$ operations (would take 3,800 years at 1 GHz).
- Held-Karp $20^2 cdot 2^{20} = 400 imes 1,048,576 pprox 4.19 imes 10^8$ operations (runs in under 1.5 seconds on a modern CPU)!
See lessHow does a Count-Min Sketch estimate heavy-hitter item frequencies under bounded RAM?
The Count-Min Sketch (CMS) is the gold-standard probabilistic algorithm for tracking frequencies in massive, unconstrained data streams (used extensively in Apache Spark, network switches, and Google search analytics). 1. Architectural Layout A Count-Min Sketch consists of a 2D matrix of integer couRead more
The Count-Min Sketch (CMS) is the gold-standard probabilistic algorithm for tracking frequencies in massive, unconstrained data streams (used extensively in Apache Spark, network switches, and Google search analytics).
1. Architectural Layout
A Count-Min Sketch consists of a 2D matrix of integer counters with
drows (depth) andwcolumns (width), paired withdindependent hash functions:2. The Operations
A. Add Item `x` (Increment):
For each row
ifrom0tod - 1, compute column indexcol = h_i(x) % w, and increment that counter:B. Query Frequency of `x` (Point Query):
Because multiple items might collide at the same counter bucket, hash collisions can only increase a counter, never decrease it!
Therefore, to get the best possible estimate, we take the MINIMUM across all d rows:
The Golden Invariant: A Count-Min Sketch NEVER underestimates the true count! True frequency is always $le$ estimated frequency.
3. Mathematical Dimensioning Rules
If you want an error bound within $epsilon cdot N$ with confidence probability $1 – delta$:
ceil pprox lceil rac{2.718}{epsilon}
ceil$
ceil$
For example, to guarantee $le 0.1%$ error with $99%$ confidence, you need only $w = 2718$ columns and $d = 5$ rows. That’s just 13,590 integer counters (~54 KB of RAM) to monitor billions of events!
Clean Python 3.12 Implementation
Complexity Breakdown
- Add Time:
- Query Time:
- Memory Footprint:
See lessO(d)— strictly constant time ($5$ hash calculations and memory writes).O(d)— strictly constant time ($5$ lookups).O(w * d)— strictly fixed in size. Bounded memory that never grows regardless of how many billions of packets arrive!How to implement Consistent Hashing with Virtual Nodes to eliminate hot spots in distributed caches?
Consistent Hashing is one of the foundational building blocks of distributed systems (used in Apache Cassandra, Amazon DynamoDB, Akamai CDN, and Envoy Proxy). 1. The Problem with Naive Modulo Hashing If you have 4 servers and use hash(key) % 4, when server 4 crashes, you now compute hash(key) % 3. BRead more
Consistent Hashing is one of the foundational building blocks of distributed systems (used in Apache Cassandra, Amazon DynamoDB, Akamai CDN, and Envoy Proxy).
1. The Problem with Naive Modulo Hashing
If you have 4 servers and use
hash(key) % 4, when server 4 crashes, you now computehash(key) % 3. Because almost every number changes its remainder modulo 3, nearly 100% of cached keys instantly miss, hammering your backend database in a catastrophic thundering herd.In Consistent Hashing, when a server is added or removed, only $1/N$ of keys need to be remapped on average. All other keys stay on their existing servers!
2. The Hash Ring & Why Virtual Nodes are Mandatory
Imagine a circular ring of numbers from $0$ to $2^{32} – 1$ (the output space of a 32-bit hash function like Murmur3 or MD5).
The Hot Spot Problem: If you only place 3 physical servers on the ring, their hash positions might be clustered close together (e.g. at 10 degrees, 25 degrees, and 280 degrees). Server 3 will end up handling 70% of all traffic, causing a massive hot spot!
The Solution (Virtual Nodes): Instead of hashing Server A once, we hash it 100 or 200 times under different labels (
"server-A#1","server-A#2", …,"server-A#200"). By distributing hundreds of virtual replicas uniformly across the 360-degree ring, standard deviations drop to near zero, and load is balanced evenly across all physical hardware.Production Python 3.12 Implementation with bisect
Complexity Breakdown
- Key Lookup:
- Node Add/Remove:
See lessO(log(R * N))using binary search, whereRis replicas (e.g. 150) andNis physical servers (e.g. 10). Searching an array of 1,500 numbers takes 11 comparisons (< 1 microsecond).O(R * log(R * N)). Adding a server only migrates keys from its immediate clockwise neighbor!Gas Station Circular Tour: Mathematical proof of why a single pass in O(N) is sufficient
The Gas Station problem is one of the most elegant examples of the Greedy Elimination Proof. Let's break down the mathematical invariant that allows you to skip stations with 100% confidence. 1. The Two Fundamental Theorems Theorem 1: Total Balance Invariant If $sum gas[i] ge sum cost[i]$, there isRead more
The Gas Station problem is one of the most elegant examples of the Greedy Elimination Proof. Let’s break down the mathematical invariant that allows you to skip stations with 100% confidence.
1. The Two Fundamental Theorems
Theorem 1: Total Balance Invariant
If $sum gas[i] ge sum cost[i]$, there is guaranteed to be at least one valid starting station that completes the entire circuit.
Why? Because the total net balance $sum (gas[i] – cost[i]) ge 0$. If you graph the cumulative fuel sum along the circle, the lowest dip (the absolute minimum point on the graph) is the optimal starting point! Starting right after that lowest dip means your tank will never dip below zero!
Theorem 2: The Greedy Skip Invariant
Suppose you start at station
Aand successfully reach stationB, but you fail to travel fromBtoB + 1(your tank drops below 0).Claim: No station
CbetweenAandB(i.e. $A le C le B$) can be the starting station!Proof:
Aand reachedC, the gas you had in your tank upon arriving atCwas $ge 0$.C, you still starved and died atB!Cfrom scratch (with an empty tank, zero bonus gas), you would run out of fuel at or before stationB!Therefore, every single station from
AtoBis mathematically disqualified in one fell swoop! The next possible candidate can only beB + 1.Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N). Exactly one single pass through the array. Zero nested loops.O(1). Exactly 3 scalar integers tracking running totals.How does a 32-bit Binary Trie find the Maximum XOR of Two Numbers in O(N) time?
The Maximum XOR problem is the ultimate showcase of how bit manipulation and trees blend together. Once you see the greedy nature of binary numbers, the Binary Trie solution becomes second nature. 1. The Greedy Bit Principle In binary numbers, the Most Significant Bit (MSB) has more numerical valueRead more
The Maximum XOR problem is the ultimate showcase of how bit manipulation and trees blend together. Once you see the greedy nature of binary numbers, the Binary Trie solution becomes second nature.
1. The Greedy Bit Principle
In binary numbers, the Most Significant Bit (MSB) has more numerical value than all lower bits combined! For example, bit 30 ($2^{30} pprox 1.07 imes 10^9$) is strictly greater than the sum of all bits from 0 to 29 combined ($2^{30} – 1$).
Therefore, to maximize an XOR sum, you must be greedy from left to right (MSB down to LSB):
numis1, you desperately want to pair it with a number whose corresponding bit is0(because1 ^ 0 = 1).numis0, you want to pair it with a number whose corresponding bit is1(because0 ^ 1 = 1).2. Why a Binary Trie?
A Binary Trie is just a tree where every node has at most two children:
0(left) and1(right).x, you walk down the Trie. At each bitb, you ask: ‘Does the opposite branch (1 - b) exist?’1.b), so that bit in your XOR result becomes0.Because you made the best possible choice at every single bit position starting from the highest power of 2, the final accumulated number is mathematically guaranteed to be the global maximum XOR!
Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(31 * N) = O(N). InsertingNnumbers takes31 * Noperations. QueryingNnumbers takes31 * Noperations. Total time is strictly linear in the number of elements.O(31 * N)worst-case node allocations. In practice, prefix branches overlap heavily, keeping memory around a few megabytes.How does Brian Kernighan’s bit algorithm work, and why does n & (n – 1) clear the lowest set bit?
The trick n & (n - 1) is one of the most elegant one-liners in computer engineering. Let's look at the exact bitwise mechanics so the mathematical proof becomes obvious. 1. What happens when you subtract 1 in binary? Think about standard base-10 math: when you subtract 1 from 1000, what happens?Read more
The trick
n & (n - 1)is one of the most elegant one-liners in computer engineering. Let’s look at the exact bitwise mechanics so the mathematical proof becomes obvious.1. What happens when you subtract 1 in binary?
Think about standard base-10 math: when you subtract 1 from
1000, what happens? The lowest non-zero digit (1) becomes 0, and all trailing zeroes become 9s:0999.Binary works exactly the same way, but with 0s and 1s:
Any positive binary integer can be written in this general form:
where the
1shown is the lowest set bit (the rightmost 1), followed by zero or more0s.When you compute
n - 1:1remains completely untouched.1turns into a0(borrowing from the subtraction).0s flip into1s!2. The Bitwise AND Operation: n & (n – 1)
Now perform a bitwise AND between
nandn - 1:Look at what happened:
prefixmatched identically → remains preserved.1was paired with0→ becomes0!0s were paired with1s → remain0!Conclusion: The operation
n & (n - 1)turns off the lowest set bit innand leaves every other bit completely unchanged. Pure mathematical magic!3. Real-World Applications
A. Counting Set Bits in O(k) time (where k is number of 1s)
Instead of looping 32 or 64 times, Brian Kernighan’s algorithm loops only as many times as there are 1-bits:
If a 64-bit integer has only two set bits, this loop executes exactly twice and terminates!
B. Instant Power of Two Check in O(1)
A power of two in binary has exactly one set bit (e.g.
8 = 1000_2,16 = 10000_2). If you strip that single bit and the result is 0, it was a power of 2:C. Hardware POPCNT Alternative
On modern x86_64 CPUs, you have the dedicated hardware assembly instruction
See lessPOPCNT(or__builtin_popcountin GCC/Clang), which computes set bits in a single CPU cycle. But when writing portable code or kernel routines without AVX/SSE guarantees, Brian Kernighan’s algorithm remains the golden standard.