This lesson on Bit Manipulation is hands-on and example-driven. You will master fundamental bitwise operations and bitmasking techniques to manipulate raw binary representations directly. You will be able to solve common algorithmic patterns—such as missing number recovery, population count, and power-of-two validation—in O(1) auxiliary space and optimal hardware clock cycles.
What You'll Be Able To Do
- Construct bitmasks using left shifts to set, clear, and toggle specific bit positions.
- Solve missing number array problems in O(N) time and O(1) space using XOR cancellation properties.
- Implement Brian Kernighan's algorithm using the n & (n - 1) identity to count set bits in O(k) time.
- Evaluate whether an integer is an exact power of two using a single constant-time bitwise check.
Detailed Concept Walkthrough
1. Bitwise Operators and Bitmasking
Bitwise operators manipulate individual bits in parallel at native CPU speeds. A bitmask acts like a surgical stencil to target and modify specific bit positions without altering surrounding bits.
- Mechanism: Create a mask for bit position k by left-shifting 1 by k spots (1 << k). Use bitwise OR (|) to force the bit to 1, bitwise AND with a bitwise NOT (~(1 << k)) to force it to 0, or bitwise XOR (^) to flip it.
- Under the Hood: Bitwise logic operations execute directly in the Arithmetic Logic Unit (ALU), typically completing in a single clock cycle with zero memory overhead.
- Best Practice: Always isolate the target bit using explicit shifts rather than arithmetic powers of two to ensure predictable constant-time hardware instruction mapping.
# Target the 5th bit (0-indexed)
mask = 1 << 5
# Force bit ON
val_on = num | mask
# Force bit OFF
val_off = num & ~mask
# Toggle bit
val_toggled = num ^ mask
Key Takeaway: Bitmasks use shifts and logic gates to surgically read, set, clear, or toggle bits in O(1) time.
2. XOR Cancellation Properties
XOR returns true only when paired bits differ, creating an identity where any number XORed with itself cancels to zero, and any number XORed with zero remains unchanged.
- Mechanism: Because a ^ a = 0 and a ^ 0 = a, identical pairs across an arbitrary sequence of XOR operations neutralize completely regardless of order (commutative and associative).
- Execution Flow: To locate a single missing value from 1 to N in an array of size N-1, XOR all expected integers from 1 to N together, then XOR every element present in the array against that cumulative value.
- Under the Hood: Matching numbers zero out every bit position simultaneously across the word, leaving only the unpaired value in O(1) extra space.
def find_missing(arr, n):
xor_sum = 0
# XOR all expected numbers 1 to n
for i in range(1, n + 1):
xor_sum ^= i
# XOR all present numbers in array
for num in arr:
xor_sum ^= num
return xor_sum # Only missing number remains
Key Takeaway: Self-cancellation (x ^ x = 0) enables instant filtering of duplicated values without auxiliary memory.
3. Brian Kernighan's Algorithm
Subtracting 1 from a binary number flips the lowest set bit and all trailing zeros, allowing n & (n - 1) to clear exactly one set bit per iteration.
- Mechanism: Subtracting 1 inverts the rightmost 1-bit to 0 and changes all following 0s to 1s. Performing a bitwise AND between n and n - 1 clears that lowest 1-bit while leaving higher bits intact.
- Under the Hood: Standard bit-counting checks all 32 or 64 bits sequentially; Kernighan's method iterates only k times, where k is the number of active set bits.
- Execution Flow: Initialize a counter to 0, execute n = n & (n - 1), increment the counter, and repeat until n becomes 0.
def count_set_bits(n):
count = 0
while n > 0:
n = n & (n - 1) # Clears the lowest set bit
count += 1
return count
Key Takeaway: The expression n & (n - 1) surgically clears the rightmost set bit in each step.
4. Constant Time Power of Two Check
A positive power of two possesses exactly one set bit in binary, meaning its predecessor shares zero overlapping set bits.
- Mechanism: For any power of two (e.g., 8 is 1000 in binary), n - 1 flips the leading 1 to 0 and all trailing 0s to 1s (e.g., 7 is 0111). Therefore, n & (n - 1) evaluates to 0.
- Syntax Rule: The check requires guarding against zero (n > 0), because 0 is not a power of two despite 0 & -1 evaluating to 0.
- Under the Hood: This eliminates loops, divisions, and floating-point logarithms, validating powers of two in a single non-branching cycle.
def is_power_of_two(n):
# Ensure positive and verify exactly one set bit
return n > 0 and (n & (n - 1)) == 0
Key Takeaway: A positive integer n is a power of two if and only if n & (n - 1) == 0.
Topics Covered in Bit Manipulation
- Missing Number Puzzle (0:00 - 0:45) — Presents the challenge of finding a missing integer from 1 to a million with zero memory.
- Hardware Bitwise Fundamentals (0:45 - 1:35) — Explains that bitwise operations run directly on CPU hardware in single clock cycles.
- Bitmask Construction (1:35 - 2:30) — Demonstrates creating bitmasks with left shifts to set, clear, and toggle specific bits.
- XOR Missing Number Solution (2:30 - 3:30) — Solves the missing number problem using XOR self-cancellation across expected and actual values.
- Brian Kernighan Algorithm (3:30 - 4:15) — Introduces the n & (n - 1) operation to count set bits in iterations proportional to set bit count.
- Power of Two Verification (4:15 - 5:00) — Explains the constant-time logic for determining whether an integer is a power of two.
DSA for Interviews Cheat Sheet
-
1 << k— Generates bitmask targeting the k-th bit positionmask = 1 << 5 -
n | (1 << k)— Sets the k-th bit to 1n = n | (1 << 3) -
n & ~(1 << k)— Clears the k-th bit to 0n = n & ~(1 << 3) -
n ^ (1 << k)— Toggles the k-th bitn = n ^ (1 << 3) -
n & (n - 1)— Clears the lowest set bit from nn = n & (n - 1) -
n > 0 and (n & (n - 1)) == 0— Checks if n is a power of 2is_pow2 = n > 0 and (n & (n - 1)) == 0
Comparison Table
| Operation | Truth Condition | Primary Utility |
|---|---|---|
| AND (&) | Both bits are 1 | Clearing bits or checking active flags |
| OR ( | ) | Either bit is 1 |
| XOR (^) | Bits are different | Toggling bits and pair cancellation |
Common Pitfalls
- Mistake: Forgetting to verify n is strictly greater than zero when checking powers of two. Avoid: Prepend the condition with n > 0 before checking n & (n - 1) == 0.
- Mistake: Neglecting operator precedence rules when combining bitwise and equality operators. Avoid: Always wrap bitwise expressions in parentheses when comparing results with relational operators.
- Mistake: Looping through all fixed word bits (32/64) to count set bits. Avoid: Use Brian Kernighans algorithm to iterate strictly over the count of set bits.
FAQs
- Why does XORing identical numbers always produce zero? XOR yields 1 only when bits differ; because identical numbers share identical bits at every position, all bit comparisons evaluate to 0.
- Why is Brian Kernighan's algorithm faster than standard bit checking? It clears one set bit per step using n & (n - 1), making iteration count equal to the number of set bits rather than total bit length.
- How does n & (n - 1) remove the rightmost set bit? Subtracting 1 turns the lowest 1-bit to 0 and inverts all lower zeros to ones, leaving zero overlapping 1-bits in that lower region when ANDed.