
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.
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.Why does Patience Sorting solve Longest Increasing Subsequence in O(N log N) instead of O(N^2)?
This is one of the most common points of confusion when studying LIS. Let's clear up the mystery of why the tails array works even though its contents look 'wrong'. 1. What does the `tails` array actually represent? In Patience Sorting (inspired by solitaire card games): tails[k] stores the SMALLESTRead more
This is one of the most common points of confusion when studying LIS. Let’s clear up the mystery of why the
tailsarray works even though its contents look ‘wrong’.1. What does the `tails` array actually represent?
In Patience Sorting (inspired by solitaire card games):
tails[k]stores the SMALLEST ending value of an increasing subsequence of lengthk + 1found so far.Why do we care about the smallest ending value? Because in an increasing subsequence, the smaller the number you end with, the easier it is for future numbers to be bigger than it! You want to keep your options as open as possible.
2. The Step-by-Step Card Dealing Analogy
Suppose our array is:
[10, 9, 2, 5, 3, 7, 101, 18].10:tails = [10](Best subsequence of len 1 ends with 10).9: 9 < 10. Replace 10 with 9:tails = [9](Ending with 9 is strictly better than ending with 10).2: 2 < 9. Replace 9 with 2:tails = [2].5: 5 > 2! Extend!tails = [2, 5](Best len 1 ends in 2, best len 2 ends in 5).3: 3 < 5. Replace 5 with 3:tails = [2, 3](Now best len 2 ends in 3!).7: 7 > 3! Extend!tails = [2, 3, 7](Len 3).101: Extend!tails = [2, 3, 7, 101](Len 4).18: 18 < 101. Replace 101 with 18:tails = [2, 3, 7, 18].Total length of
tailsis 4. The answer is 4!3. Why the array contents might look scrambled, but length is ALWAYS correct
Imagine if after
[2, 3, 7, 18]we saw1. We would replace2with1, resulting intails = [1, 3, 7, 18].Notice that
[1, 3, 7, 18]might not be a valid subsequence from the original array. And that doesn’t matter!Replacing
2with1only prepares the board for a hypothetical future subsequence that starts with1. It does not change the fact that a valid subsequence of length 4 ([2, 3, 7, 18]) was already locked in!The length of
tailsonly increases when a number is strictly greater than ALL existing tail values. Replacements never shrink the array length!Clean Python 3.12 Implementation with bisect_left
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N log N). We iterate throughNelements, and for each element we perform binary search overtails(at most lengthN).N * log(N). For100,000elements, this finishes in 0.02 seconds (compared to ~45 seconds forO(N^2)).O(N)to hold thetailsarray.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.Binary Search on Answer: How to solve Koko Eating Bananas without floating-point bugs?
Binary Search on Answer Space is one of the highest-leverage algorithmic patterns you can learn. Once you recognize it, dozens of seemingly hard problems (shipping packages, splitting arrays, cutting ribbons, allocating memory) all collapse into the exact same 15 lines of code. 1. When Can You Use TRead more
Binary Search on Answer Space is one of the highest-leverage algorithmic patterns you can learn. Once you recognize it, dozens of seemingly hard problems (shipping packages, splitting arrays, cutting ribbons, allocating memory) all collapse into the exact same 15 lines of code.
1. When Can You Use This Pattern?
Ask yourself one simple question: Is the condition monotonic?
k = 100bananas/hour and succeeds in finishing in underhhours, would eating at speedk = 101also succeed? Yes, always.k = 5is too slow and fails, would eating at speedk = 4also fail? Yes, always.Because the outcome transitions cleanly from
False, False, ..., True, True, True, the answer space is monotonic. That means we don’t need to test every speed from 1 to 1 billion linearly—we can binary search it inO(log(MaxPile))steps!2. The Integer Ceiling Trick (Say Goodbye to Float Bugs)
If Koko has a pile of
7bananas and eats at speedk = 3, she needsceil(7 / 3) = 3hours.In Python or C++, doing
math.ceil(pile / k)converts the numbers to IEEE-754 64-bit floats. On massive numbers (e.g.10^14), floating-point precision degrades, causing silent off-by-one errors.The standard integer arithmetic replacement for
ceil(a / b)is:Let’s test it:
(7 + 3 - 1) // 3 = 9 // 3 = 3. Exactly right, 100% integer math, zero float conversions!3. Clean Python 3.12 Implementation
4. Complexity & Production Benchmarks
- Time Complexity:
- Space Complexity:
- Overflow Note for C++ / Java: In C++,
See lessO(N * log(M))whereNis the number of piles andMismax(piles). IfM = 10^9,log2(10^9) ≈ 30. Even with 100,000 piles, the validation function runs at most 30 times. Total operations: ~3 million, executing in under 15 milliseconds.O(1)auxiliary memory.total_hourscan easily exceed2^31 - 1if speeds are small and piles are large. Always declareint64_t total_hours = 0;to prevent integer overflow.How does a Monotonic Stack solve Largest Rectangle in Histogram in a single pass?
The Largest Rectangle in Histogram is famous because it feels like magic until you see the visual geometry behind it. Let's demystify it once and for all. 1. The Core Realization For any bar at index k with height H = heights[k], what is the widest rectangle you can make using H as the height? The rRead more
The Largest Rectangle in Histogram is famous because it feels like magic until you see the visual geometry behind it. Let’s demystify it once and for all.
1. The Core Realization
For any bar at index
kwith heightH = heights[k], what is the widest rectangle you can make usingHas the height?The rectangle can extend as far left as possible until it hits a bar shorter than
H, and as far right as possible until it hits another bar shorter thanH.So the entire problem boils down to finding two things for every bar:
2. Why a Monotonic Increasing Stack?
A monotonic stack keeps indices of bars whose heights are strictly increasing:
[2, 4, 6, 8].As long as the next bar is taller or equal, the rectangle could potentially keep growing, so we just push its index onto the stack.
The Trigger: The moment you encounter a bar that is shorter than the top of the stack (say we see a bar of height
3when the stack top is8), you have found the Right Boundary for that8! The bar of height8cannot extend any further to the right. Its journey is finished.When you pop
8:iis its first shorter bar on the right.Therefore, the width of the rectangle bounded by height
His simply:width = (i - stack[-1] - 1).3. Clean Python 3.12 Implementation with Sentinel Trick
4. Step-by-Step Trace with Numbers
Let’s trace
heights = [2, 1, 5, 6, 2, 3]with sentinel[2, 1, 5, 6, 2, 3, 0]:i = 0 (h=2): Stack =[0]i = 1 (h=1): 1 < 2! Pop0(h=2). Stack empty → width = 1. Area =2 * 1 = 2. Push 1. Stack =[1].i = 2 (h=5): 5 > 1. Push 2. Stack =[1, 2].i = 3 (h=6): 6 > 5. Push 3. Stack =[1, 2, 3].i = 4 (h=2): 2 < 6!3(h=6): right = 4, left = 2 → width =4 - 2 - 1 = 1. Area =6 * 1 = 6.2(h=5): right = 4, left = 1 → width =4 - 1 - 1 = 2. Area =5 * 2 = 10!Push 4. Stack =
[1, 4].0sentinel cleanly flushes all remaining elements.Max Area = 10 (from bars of height 5 and 6).
5. Why is this strictly O(N)?
Even though there is a
See lesswhileloop inside theforloop, every index is pushed onto the stack exactly once and popped from the stack at most once. Total operations across the entire array are at most2N. That is a rock-solid, linearO(N)runtime withO(N)memory.Why does the Two-Pointer approach beat Monotonic Stack for Trapping Rainwater in production?
I see this question come up all the time in engineering interviews and production optimizations. The short answer is: memory allocations and CPU cache locality. On paper, both the Monotonic Stack and Two Pointers are O(N) time. But in reality: Monotonic Stack: Pushes and pops indices into a dynamicRead more
I see this question come up all the time in engineering interviews and production optimizations. The short answer is: memory allocations and CPU cache locality.
On paper, both the Monotonic Stack and Two Pointers are
O(N)time. But in reality:std::stackin C++ or a dynamic slice in Python/Go). That means repeated memory allocations, pointer indirection, and cache misses every time the stack resizes or wanders through heap memory.left,right,left_max,right_max). These variables stay entirely inside CPU registers. There is zero heap allocation, zero pointer chasing, and the CPU prefetcher streams the array from both ends sequentially at full hardware bus speed.The Plain English Intuition
Think about standing at the edge of a swimming pool. The amount of water that can sit on top of any single column
iis strictly decided by one thing: the shorter of the two tallest walls on its left and right.Mathematically:
water[i] = max(0, min(max_left, max_right) - height[i]).Here is the genius of two pointers: you place one pointer at the start (
left) and one at the end (right). At every step:height[left] <= height[right], you know for certain that whatever tall wall exists on the far right is at least as tall asheight[left]. So the bottleneck for the left side is only determined byleft_max. You can safely calculate water atleftand moveleft++.height[right] < height[left], the exact opposite holds true. The bottleneck forrightis determined purely byright_max. You calculate water atrightand moveright--.You never have to look back, and you never have to store past heights in a stack!
Production-Ready Python 3.12 Implementation
Clean C++20 Version (Zero Allocations)
Complexity & Production Pitfalls
- Time Complexity:
- Space Complexity:
- 32-bit Integer Overflow: If you have an array of
See lessO(N). Every element is visited exactly once. No nested loops.O(1). No auxiliary memory allocated.100,000elements, each with height100,000, the total water can reach10^10. A standard 32-bit signed integer will overflow and return a negative number! Always use a 64-bit integer (int64_tin C++ orlongin Java) for the accumulator.How to rotate an array in-place with O(1) space and zero cache misses?
This is a classic problem where the textbook solution and the production solution diverge. When you are moving 10 million integers in memory, allocating a temp slice or doing naive cyclic swaps will kill your performance due to cache misses. The cleanest, most battle-tested way to do this in productRead more
This is a classic problem where the textbook solution and the production solution diverge. When you are moving 10 million integers in memory, allocating a temp slice or doing naive cyclic swaps will kill your performance due to cache misses.
The cleanest, most battle-tested way to do this in production is the 3-Reversal Trick (often called the Reversal Algorithm). It requires zero extra memory and traverses contiguous memory sequentially, which modern CPU prefetchers love.
1. The Intuition (Why 3 Reversals Work)
Suppose you have the array
[1, 2, 3, 4, 5, 6, 7]and you want to rotate right byk = 3(so[5, 6, 7, 1, 2, 3, 4]).Notice the split: the last
kelements need to move to the front, and the firstn - kelements move to the back. If you reverse the whole thing first, everything is in the right neighborhood but backwards:[7, 6, 5, 4, 3, 2, 1][5, 6, 7, 4, 3, 2, 1][5, 6, 7, 1, 2, 3, 4]Done! Every element is now in its exact final position.
2. Production C++20 Implementation
3. Python 3.12 Clean Version
4. Complexity Breakdown
O(N)total time. Step 1 doesn/2swaps, Step 2 doesk/2swaps, and Step 3 does(n-k)/2swaps. Total swaps = exactlynswaps. You can't beat linear time because every element must change position.O(1)auxiliary space. Just two index pointers living directly in CPU registers.5. Real-World Gotchas to Watch Out For
- When k > n: Always take
- Negative k (Left Rotation): If your system asks for a left rotation by
- Empty or Single Element Arrays: Check
See lessk = k % n. Forgetting this causes out-of-bounds pointer crashes whenk = 15on an array of length 5.k, simply transform it: a left rotation bykis equivalent to a right rotation by(n - (k % n)) % n.n <= 1upfront to prevent unsigned integer underflow onn - 1.