Bit Manipulation: Code Templates in 6 Languages
If you have not read the concept guide yet, start there for the intuition and complexity analysis. This page gives you the code you can memorize and adapt during an interview. Every template here runs in
Main Template: XOR Cancellation
This is the most famous bit manipulation pattern in coding interviews. XOR has two properties that make it invaluable: x ^ x = 0 (a number XORed with itself cancels to zero) and x ^ 0 = x (a number XORed with zero stays unchanged). By XORing every element in an array together, duplicates cancel out and only the unique element remains.
Use this for Single Number .
// XOR every element. Pairs cancel to 0, leaving the
// unique value. Works because XOR is commutative and
// associative so order does not matter.
function singleNumber(nums) {
let result = 0;
for (const num of nums) {
result ^= num;
}
return result;
}Code Breakdown
Key Variables
result: the accumulator. Starts at 0 because x ^ 0 = x, so the first element seeds the accumulator without special-case logic. After processing every element,resultholds the value that appeared an odd number of times.
Visual Mechanism
stateDiagram-v2
[*] --> Init: result = 0
Init --> Process: for each num in nums
Process --> XOR: result ^= num
XOR --> Process: next num
Process --> Done: no more nums
Done --> [*]: return result
Critical Sections
The XOR accumulator is the entire algorithm. No conditionals, no extra data structures. The key insight is that XOR is commutative and associative, so the order of elements does not matter. Each duplicate pair produces 0 regardless of where it appears in the array, and the unique element survives because it is XORed with 0 at the end.
This pattern extends naturally. Missing Number (problem 3) XORs both indices and values to find the missing element. The same accumulator approach works with a second loop or a different input structure, but the core operation never changes.
Variations
1. Bit Manipulation Utilities
These are the building blocks for any bit-level problem. Each is a one-liner that replaces several lines of conditional logic. Use these when a problem asks you to read, write, or count bits directly.
// Check if bit k is set (1)
function checkBit(n, k) {
return (n & (1 << k)) !== 0;
}
// Set bit k to 1
function setBit(n, k) {
return n | (1 << k);
}
// Clear bit k (set to 0)
function clearBit(n, k) {
return n & ~(1 << k);
}
// Toggle bit k (0 to 1, 1 to 0)
function toggleBit(n, k) {
return n ^ (1 << k);
}
// Check if n is a power of two
function isPowerOfTwo(n) {
// Powers of two have exactly one bit set.
// n-1 flips that bit and clears everything
// below it, so n & (n-1) is 0.
return n > 0 && (n & (n - 1)) === 0;
}
// Count set bits (Brian Kernighan's Algorithm)
// Time: O(number of set bits)
function countSetBits(n) {
let count = 0;
while (n !== 0) {
n = n & (n - 1); // clear the lowest set bit
count++;
}
return count;
}Visual: How n & (n - 1) Works
graph LR
I["n = 12 (1100)"] --> S["n - 1 = 11 (1011)"]
I --> A["n & (n - 1)"]
S --> A
A --> R["Result = 8 (1000)"]
Subtracting 1 flips the lowest set bit to 0 and all lower bits to 1. ANDing with the original clears that bit while leaving higher bits unchanged. Repeating this counts each set bit exactly once.
Visual: Bit Operations on a 4-bit Integer
graph TD
subgraph Original
O["n = 5 (0101)"]
end
subgraph Check
C["Bit 2? (0101) & (0100) = 0100 != 0 => true"]
end
subgraph Set
S["Set bit 1: 0101 | 0010 = 0111 (7)"]
end
subgraph Clear
CL["Clear bit 2: 0101 & 1011 = 0001 (1)"]
end
subgraph Toggle
T["Toggle bit 0: 0101 ^ 0001 = 0100 (4)"]
end
O --> C
O --> S
O --> CL
O --> T
2. Subset Generation
A bitmask represents each subset as a binary number. For an array of n elements, there are 2^n subsets. Mask i has its j-th bit set when element j belongs to the subset. This is how DP over subsets internalizes state.
Use this for Subsets .
// Time: O(N * 2^N) Space: O(1) excluding output
function subsets(nums) {
const output = [];
const n = nums.length;
// Each mask from 0 to 2^n - 1 represents one subset.
// Bit j being set means nums[j] is included.
for (let mask = 0; mask < (1 << n); mask++) {
const subset = [];
for (let j = 0; j < n; j++) {
if ((mask & (1 << j)) !== 0) {
subset.push(nums[j]);
}
}
output.push(subset);
}
return output;
}Visual
graph LR
M0["Mask 0 (000): []"] --> M1["Mask 1 (001): [a]"]
M1 --> M2["Mask 2 (010): [b]"]
M2 --> M3["Mask 3 (011): [a, b]"]
M3 --> M4["Mask 4 (100): [c]"]
M4 --> M5["..."]
Now head to the practice problems to apply these templates to real interview questions.