Foundations
Bit Manipulation
Think in binary. XOR, AND, shifts - a small toolkit that solves a surprising range of problems.
What is bit manipulation?
Every integer is stored in binary - a sequence of 0s and 1s. Bit manipulation operates on those individual bits using hardware-level operators (AND, OR, XOR, shift) instead of arithmetic. These operations are O(1) per call and often collapse problems that look O(n) into a single pass with O(1) extra space.
Python integers are arbitrary precision, so there is no 32-bit overflow to worry about. The main quirk is ~n = -(n+1) rather than a simple bit flip, because Python extends the sign bit infinitely. In Java and C++ interviews you must account for 32-bit sign bits and integer overflow, so it is worth noting the difference when asked in a cross-language context.
Bit manipulation is not a general-purpose technique. It appears in a specific cluster of problems: finding the unique element among duplicates (XOR cancellation), checking or setting flags (AND/OR with masks), counting set bits (popcount), and generating all subsets via bitmask enumeration.
The most important operator to internalize is XOR (^): a ^ a = 0 (self-inverse - any number cancels itself), a ^ 0 = a (identity - zero changes nothing), and XOR is commutative and associative (order never matters). These three properties together mean XORing a list of numbers cancels every even-count duplicate and leaves only the unique element.
Core operations
| Operation | Symbol | Example | Notes |
|---|---|---|---|
| AND | & | 5 & 3 = 1 (101 & 011 = 001) | Both bits must be 1; used for masking |
| OR | | | 5 | 3 = 7 (101 | 011 = 111) | Either bit is 1; used for setting bits |
| XOR | ^ | 5 ^ 3 = 6 (101 ^ 011 = 110) | Bits differ; used for toggling and cancellation |
| NOT | ~ | ~5 = -6 | Flips all bits; in Python ~n = -(n+1) |
| Left shift | << | 1 << 3 = 8 | Multiply by 2^k |
| Right shift | >> | 16 >> 2 = 4 | Divide by 2^k (floor) |
Key patterns
XOR cancellation
XOR every element together. Duplicate pairs cancel to 0; the single unique element remains. This works because XOR is commutative, associative, self-inverse (n ^ n = 0), and has 0 as its identity (n ^ 0 = n), so order of XOR never matters.
Example: the single number. Folding [4, 1, 2, 1, 2] through XOR: the running result goes 4, 5, 7 as the values land, then the second 1 flips it back to 6 and the second 2 flips it to 4 - each duplicate pair cancels itself to 0. The lone 4 is never cancelled, so the final result is 4. The visualizer below shows the binary of result after every step.
n ^ n ^ n = n, not 0. Confirm the problem guarantees even-count duplicates before applying this trick.Power-of-two check
A power of two has exactly one bit set: 1, 10, 100, 1000. Subtracting 1 flips that bit and sets all lower bits: 8 - 1 = 7 means 1000 - 1 = 0111. ANDing n & (n-1) clears the lowest set bit; if the result is 0, exactly one bit was set.
Example: is it a power of two?. Test 16 (10000): 16 - 1 = 15 is 01111, the two share no set bits, and 16 & 15 = 0, so yes. Test 18 (10010): subtracting 1 flips only the lowest set bit, giving 17 (10001), and 18 & 17 = 16, a non-zero leftover that exposes the second set bit - so no. The visualizer below lines up the three bit rows for either n.
n > 0 - 0 & (0 - 1) evaluates to 0 in Python due to arbitrary-precision arithmetic, which would incorrectly classify 0 as a power of two without the guard.Bit masking
Use a mask 1 << k to target bit position k (0-indexed from the right). Four independent operations - get, set, clear, toggle - each takes one line and one mask.
Example: read and flip one bit. Take n = 20 (10100) and k = 2: the get reads bit 2 as 1, set ORs in 1 << 2 = 4 but bit 2 is already 1 so n stays 20, and clear ANDs against ~4, dropping exactly that bit to give 16 (10000). Toggle XORs the same mask and flips the bit off, also landing on 16. The visualizer below highlights bit k of num as each mask lands.
n & (n-1) clears the lowest set bit; n & (-n) isolates it - these two companion tricks appear constantly. n & (n-1) removes the lowest 1-bit (used in popcount loops). n & (-n) returns a number with only the lowest 1-bit of n set (used in Fenwick trees and to iterate over set bits one by one).Bitmask enumeration
For a set of n elements, every subset corresponds to an n-bit integer from 0 to 2^n - 1. Bit k is set if and only if element k is in the subset. Iterate all 2^n subsets by looping masks from 0 to (1 << n) - 1.
Example: all subsets of [a, b, c]. Masks 0 through 7 cover all 2^3 combinations: mask 5 is binary 101, and bit 0 picks a while bit 2 picks c, yielding {a, c}; mask 0 picks nothing (the empty set) and mask 7 picks everything. The loop reads each mask's bits low to high and builds the subset in one pass. The visualizer below walks mask 0 to 7, showing which bits select which elements.
When to reach for bit manipulation
- The problem says "all elements appear twice except one" or "find the element that appears once" - XOR cancellation.
- You need to check if a number is a power of two or count how many times a number is divisible by 2.
- The problem involves subset enumeration where n is small (
n <= 20) - iterate bitmasks from 0 to 2^n - 1. - You need to pack multiple boolean flags into a single integer - permissions, visited states in bitmask DP.
- The problem involves swapping two values without a temp variable -
a ^= b; b ^= a; a ^= b. - The problem mentions XOR explicitly in the statement, or asks about Hamming distance between two numbers.
- A DP state tracks which elements have been visited and n is small - encode the state as a bitmask integer.
Done reading? Mark it so it sticks in your dashboard.