function subsetsUsingBitmask(nums) { const n = nums.length; const result = []; // Iterate through all possible subsets (2^n) for (let mask = 0; mask < (1 << n); mask++) { const subset = []; // Check each bit position for (let i = 0; i < n; i++) { // If bit i is set, include nums[i] if ((mask >> i) & 1) { subset.push(nums[i]); } } result.push(subset); } return result;}console.log(subsetsUsingBitmask([1, 2, 3]));// [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]// Mask representation:// 000 (0) → []// 001 (1) → [1]// 010 (2) → [2]// 011 (3) → [1,2]// 100 (4) → [3]// 101 (5) → [1,3]// 110 (6) → [2,3]// 111 (7) → [1,2,3]
Note: Bitmask subset enumeration is memory efficient (O(1) space for mask) and allows fast subset operations using bitwise operators. Perfect for problems with n ≤ 20 elements.
2. Generate All Subsets Using Bits
Operation
Bitwise Formula
Example
Application
Count Subsets
2^n = 1 << n
n=3 → 8 subsets
Total number of subsets
Iterate Submasks
for (s = mask; s; s = (s-1) & mask)
Submasks of 101 → 101, 100, 001
Enumerate all submasks of mask
Power of 2 Check
n & (n-1) === 0
8 (1000) & 7 (0111) = 0
Detect exact power of 2
Lowest Set Bit
n & -n
12 (1100) → 4 (0100)
Extract rightmost 1-bit
Remove Lowest Bit
n & (n-1)
12 (1100) → 8 (1000)
Clear rightmost 1-bit
Example: Iterate All Submasks of a Mask
function iterateSubmasks(mask) { const submasks = []; // Standard submask iteration trick for (let s = mask; s > 0; s = (s - 1) & mask) { submasks.push(s); } // Don't forget empty submask (0) submasks.push(0); return submasks;}console.log(iterateSubmasks(5)); // 5 = 101 in binary// Output: [5, 4, 1, 0]// 101 → 100 → 001 → 000// Why this works:// mask = 101// s-1 flips all bits from rightmost 1// & mask keeps only bits that are in original mask// 101 - 1 = 100, 100 & 101 = 100// 100 - 1 = 011, 011 & 101 = 001// 001 - 1 = 000, 000 & 101 = 000 (loop ends)
Example: DP with Bitmask - Traveling Salesman Problem
function tsp(dist) { const n = dist.length; const VISITED_ALL = (1 << n) - 1; const dp = Array(1 << n).fill(null).map(() => Array(n).fill(Infinity)); // Start from city 0 dp[1][0] = 0; // Iterate all subsets for (let mask = 1; mask <= VISITED_ALL; mask++) { for (let last = 0; last < n; last++) { // If last city not in mask, skip if (!(mask & (1 << last))) continue; // Previous mask without last city const prevMask = mask ^ (1 << last); // Try all previous cities for (let prev = 0; prev < n; prev++) { if (prevMask & (1 << prev)) { dp[mask][last] = Math.min( dp[mask][last], dp[prevMask][prev] + dist[prev][last] ); } } } } // Return to city 0 from any city let minCost = Infinity; for (let i = 1; i < n; i++) { minCost = Math.min(minCost, dp[VISITED_ALL][i] + dist[i][0]); } return minCost;}const dist = [ [0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]];console.log(tsp(dist)); // 80
3. Counting Set Bits (Brian Kernighan's Algorithm)
Method
Algorithm
Time
Description
Brian Kernighan
n &= (n-1) in loop
O(k) k=set bits
Clears rightmost set bit each iteration
Naive Loop
Check each bit with >>
O(log n)
Loop through all 32/64 bits
Built-in
n.toString(2).split('1').length-1
O(log n)
Convert to binary string and count '1's
Lookup Table
Precompute counts for 0-255
O(1)
Split number into bytes, lookup each
Parallel Count
SWAR (SIMD Within A Register)
O(1)
Count bits in parallel using bit tricks
Example: Brian Kernighan's Algorithm
function countSetBits(n) { let count = 0; // Each iteration removes rightmost set bit while (n) { n &= (n - 1); count++; } return count;}console.log(countSetBits(13)); // 3 (1101 has 3 ones)console.log(countSetBits(7)); // 3 (0111 has 3 ones)// Trace for n=13 (1101):// Iteration 1: 1101 & 1100 = 1100, count=1// Iteration 2: 1100 & 1011 = 1000, count=2// Iteration 3: 1000 & 0111 = 0000, count=3// Why n & (n-1) removes rightmost set bit:// n = ...10110100// n-1 = ...10110011 (flips bits after rightmost 1)// & = ...10110000 (rightmost 1 cleared)
Example: Count Bits for All Numbers 0 to n (DP)
function countBits(n) { const result = new Array(n + 1); result[0] = 0; for (let i = 1; i <= n; i++) { // Key insight: count[i] = count[i >> 1] + (i & 1) // Right shift removes last bit, &1 checks if last bit was 1 result[i] = result[i >> 1] + (i & 1); } return result;}console.log(countBits(5)); // [0, 1, 1, 2, 1, 2]// 0: 0000 → 0 bits// 1: 0001 → 1 bit// 2: 0010 → 1 bit// 3: 0011 → 2 bits// 4: 0100 → 1 bit// 5: 0101 → 2 bits// Alternative DP: result[i] = result[i & (i-1)] + 1// i & (i-1) removes rightmost set bitfunction countBitsAlt(n) { const result = new Array(n + 1); result[0] = 0; for (let i = 1; i <= n; i++) { result[i] = result[i & (i - 1)] + 1; } return result;}
4. XOR Patterns (Single Number Problem)
Property
Formula
Example
Application
Self XOR
a ^ a = 0
5 ^ 5 = 0
Cancel out duplicates
XOR with 0
a ^ 0 = a
5 ^ 0 = 5
Identity property
Commutative
a ^ b = b ^ a
3 ^ 5 = 5 ^ 3
Order doesn't matter
Associative
(a ^ b) ^ c = a ^ (b ^ c)
Can group any way
Chain XOR operations
Find Unique
result = a1 ^ a2 ^ ... ^ an
All pairs cancel
Find single non-duplicate
Swap Without Temp
a^=b; b^=a; a^=b
Swap two variables
In-place swap
Example: Single Number - Find Unique Element
function singleNumber(nums) { let result = 0; // XOR all numbers: duplicates cancel out for (const num of nums) { result ^= num; } return result;}console.log(singleNumber([4, 1, 2, 1, 2])); // 4// 0 ^ 4 ^ 1 ^ 2 ^ 1 ^ 2// = 4 ^ (1 ^ 1) ^ (2 ^ 2)// = 4 ^ 0 ^ 0// = 4
Example: Single Number II - Element Appears Thrice
function singleNumberII(nums) { let ones = 0, twos = 0; for (const num of nums) { // Add to twos if already in ones twos |= ones & num; // XOR with ones ones ^= num; // Remove from both if appears 3 times const threes = ones & twos; ones &= ~threes; twos &= ~threes; } return ones;}console.log(singleNumberII([2, 2, 3, 2])); // 3// All numbers appear 3 times except one// Alternative using bit countingfunction singleNumberIIBitCount(nums) { let result = 0; // Count bits at each position for (let i = 0; i < 32; i++) { let sum = 0; for (const num of nums) { sum += (num >> i) & 1; } // If count not divisible by 3, unique number has this bit if (sum % 3 !== 0) { result |= (1 << i); } } return result;}
Example: Single Number III - Two Unique Elements
function singleNumberIII(nums) { // XOR all numbers: result = a ^ b (two unique numbers) let xorAll = 0; for (const num of nums) { xorAll ^= num; } // Find rightmost set bit (where a and b differ) const rightmostBit = xorAll & -xorAll; // Partition into two groups based on this bit let a = 0, b = 0; for (const num of nums) { if (num & rightmostBit) { a ^= num; } else { b ^= num; } } return [a, b];}console.log(singleNumberIII([1, 2, 1, 3, 2, 5])); // [3, 5]// Step 1: 1^2^1^3^2^5 = 3^5 = 6 (110)// Step 2: rightmost bit = 6 & -6 = 2 (010)// Step 3: Partition by bit 1// Group 1 (bit set): 2,3,2 → 3// Group 2 (bit not set): 1,1,5 → 5
5. Bit Manipulation Tricks and Fast Operations
Operation
Bit Trick
Equivalent
Use Case
Multiply by 2k
n << k
n * (2^k)
Fast multiplication by power of 2
Divide by 2k
n >> k
Math.floor(n / (2^k))
Fast division by power of 2
Check Even/Odd
n & 1
n % 2
0=even, 1=odd
Modulo Power of 2
n & (2^k - 1)
n % (2^k)
Fast modulo by power of 2
Absolute Value
(n ^ (n >> 31)) - (n >> 31)
Math.abs(n)
Branchless absolute value
Min of Two
b ^ ((a ^ b) & -(a < b))
Math.min(a, b)
Branchless minimum
Max of Two
a ^ ((a ^ b) & -(a < b))
Math.max(a, b)
Branchless maximum
Sign Function
(n >> 31) | ((-n) >>> 31)
-1 if n<0, 0 if n=0, 1 if n>0
Get sign without branches
Next Power of 2
Decrease by 1, set all bits right of MSB, add 1
Find next 2k ≥ n
Round up to power of 2
Isolate Rightmost 1
n & -n
Get lowest set bit
Extract least significant bit
Isolate Rightmost 0
~n & (n+1)
Get lowest unset bit
Find first zero bit
Turn Off Rightmost 1
n & (n-1)
Clear lowest set bit
Remove least significant bit
Turn On Rightmost 0
n | (n+1)
Set lowest unset bit
Set first zero bit
Example: Common Bit Tricks
// Check if power of 2function isPowerOfTwo(n) { return n > 0 && (n & (n - 1)) === 0;}// Get next power of 2function nextPowerOfTwo(n) { if (n <= 0) return 1; n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; return n + 1;}// Swap two numbers without temp variablefunction swap(a, b) { console.log(`Before: a=${a}, b=${b}`); a ^= b; b ^= a; a ^= b; console.log(`After: a=${a}, b=${b}`);}// Check if opposite signsfunction oppositeSign(a, b) { return (a ^ b) < 0;}console.log(isPowerOfTwo(16)); // trueconsole.log(nextPowerOfTwo(10)); // 16swap(5, 10); // Before: a=5, b=10, After: a=10, b=5console.log(oppositeSign(5, -3)); // trueconsole.log(oppositeSign(5, 3)); // false
Example: Fast Modulo and Multiplication
function fastOperations() { const n = 25; // Multiply by 8 (2^3) console.log(n << 3); // 200 (same as n * 8) // Divide by 4 (2^2) console.log(n >> 2); // 6 (same as Math.floor(n / 4)) // Check even/odd console.log(n & 1); // 1 (odd) console.log(24 & 1); // 0 (even) // Modulo 16 (2^4) console.log(n & 15); // 9 (same as n % 16, where 15 = 16-1) // Isolate rightmost set bit console.log(12 & -12); // 4 (1100 → 0100) // Remove rightmost set bit console.log(12 & 11); // 8 (1100 → 1000)}fastOperations();
Note: While bit tricks are fast, modern compilers often optimize standard operations automatically. Use bit tricks for clarity and specific algorithms (like counting bits, XOR problems), not general arithmetic unless profiling shows benefit.
6. Hamming Distance (Bit Difference Count)
Concept
Definition
Formula
Application
Hamming Distance
Number of positions where bits differ
countBits(x ^ y)
Measure bit difference between numbers
XOR for Difference
XOR highlights different bits
x ^ y gives 1 where bits differ
Find positions of difference
Total Hamming
Sum of all pairwise distances
Count contribution of each bit position
Analyze array bit differences
Hamming Weight
Number of 1-bits in number
countBits(n)
Also called population count
Example: Hamming Distance Between Two Numbers
function hammingDistance(x, y) { // XOR to find differing bits, then count them let xor = x ^ y; let distance = 0; // Brian Kernighan's algorithm while (xor) { xor &= (xor - 1); distance++; } return distance;}console.log(hammingDistance(1, 4)); // 2// 1 = 0001// 4 = 0100// XOR = 0101 (2 bits set)
Example: Total Hamming Distance of Array
function totalHammingDistance(nums) { let total = 0; const n = nums.length; // Check each bit position (0-31 for 32-bit integers) for (let bit = 0; bit < 32; bit++) { let countOnes = 0; // Count how many numbers have this bit set for (const num of nums) { if ((num >> bit) & 1) { countOnes++; } } // Numbers with 1 * numbers with 0 = pairs with different bit const countZeros = n - countOnes; total += countOnes * countZeros; } return total;}console.log(totalHammingDistance([4, 14, 2])); // 6// 4 = 0100// 14 = 1110// 2 = 0010//// Bit 0: [0,0,0] → 0*3 = 0// Bit 1: [0,1,1] → 1*2 = 2// Bit 2: [1,1,1] → 3*0 = 0// Bit 3: [0,1,0] → 1*2 = 2// Total = 0+2+0+2+2 = 6
7. Check Power of Two Using Bits
Check
Technique
Why It Works
Edge Cases
Is Power of 2
n > 0 && (n & (n-1)) === 0
Power of 2 has exactly one bit set
n=0 is NOT power of 2
Count Check
countBits(n) === 1
Exactly one 1-bit present
Slower but more explicit
Is Power of 4
(n & (n-1)) === 0 && (n & 0x55555555)
Power of 2 with bit at even position
0x55...5 = 01010101... pattern
Next Power of 2
Set all bits right of MSB, then add 1
Creates number like 2k-1, then +1
Handle n ≤ 0 specially
Example: Power of Two Variations
function isPowerOfTwo(n) { return n > 0 && (n & (n - 1)) === 0;}function isPowerOfFour(n) { // Must be power of 2 AND bit at even position (0,2,4,6,...) // 0x55555555 = 01010101010101010101010101010101 (bits at even positions) return n > 0 && (n & (n - 1)) === 0 && (n & 0x55555555) !== 0;}function nextPowerOfTwo(n) { if (n <= 1) return 1; // Decrease by 1 to handle exact powers of 2 n--; // Set all bits to the right of MSB n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; // Add 1 to get next power of 2 return n + 1;}console.log(isPowerOfTwo(16)); // true (10000)console.log(isPowerOfTwo(18)); // false (10010)console.log(isPowerOfFour(16)); // true (16 = 4^2)console.log(isPowerOfFour(8)); // false (8 = 2^3, not 4^k)console.log(nextPowerOfTwo(10)); // 16console.log(nextPowerOfTwo(16)); // 16 (already power of 2)
Example: Find Missing Power of Two
function missingPowerOfTwo(arr, n) { // Array contains all powers of 2 from 2^0 to 2^n except one // Use XOR to find missing let xor = 0; // XOR all elements in array for (const num of arr) { xor ^= num; } // XOR all powers of 2 from 0 to n for (let i = 0; i <= n; i++) { xor ^= (1 << i); } return xor; // Missing number}// Array has [1,2,8,16,32] - missing 4console.log(missingPowerOfTwo([1, 2, 8, 16, 32], 5)); // 4
function binaryToGray(binary) { return binary ^ (binary >> 1);}function grayToBinary(gray) { let binary = gray; // XOR with all right-shifted versions while (gray >>= 1) { binary ^= gray; } return binary;}// Alternative: recursive reflection methodfunction grayCodeRecursive(n) { if (n === 0) return [0]; if (n === 1) return [0, 1]; // Get previous Gray codes const prev = grayCodeRecursive(n - 1); const result = [...prev]; // Reflect and prepend 1 (add 2^(n-1)) const prefix = 1 << (n - 1); for (let i = prev.length - 1; i >= 0; i--) { result.push(prefix | prev[i]); } return result;}console.log(binaryToGray(6)); // 5 (110 → 101)console.log(grayToBinary(5)); // 6 (101 → 110)console.log(grayCodeRecursive(3)); // [0,1,3,2,6,7,5,4]
9. Gosper's Hack (Next Combination with Same Set Bits)
Technique
Purpose
How It Works
Complexity
Gosper's Hack
Generate next k-combination in lexicographic order
Find next number with same bit count
O(1) per iteration
Pattern
Iterate all n choose k combinations
Maintains same number of set bits
Total: O(C(n,k))
Formula
c = x & -x; r = x + c; x = (((r^x)>>2)/c)|r
Finds next combination efficiently
Bitwise arithmetic only
Example: Gosper's Hack - Enumerate k-Combinations
function gospersHack(n, k) { const combinations = []; // Start with k rightmost bits set: 2^k - 1 let set = (1 << k) - 1; const limit = 1 << n; while (set < limit) { combinations.push(set); // Gosper's hack: find next combination const c = set & -set; // Rightmost set bit const r = set + c; // Add to create carry set = (((r ^ set) >> 2) / c) | r; } return combinations;}// Get all 3-bit combinations from 5 bitsconsole.log(gospersHack(5, 3));// [7, 11, 13, 14, 19, 21, 22, 25, 26, 28]// Binary representations all have exactly 3 bits set:// 00111 (7)// 01011 (11)// 01101 (13)// 01110 (14)// 10011 (19)// ...// Extract indices of set bitsfunction getIndices(mask) { const indices = []; let i = 0; while (mask) { if (mask & 1) indices.push(i); mask >>= 1; i++; } return indices;}const combs = gospersHack(5, 3);console.log(combs.map(getIndices));// [[0,1,2], [0,1,3], [0,2,3], [1,2,3], [0,1,4], ...]
Example: Apply Gosper's Hack to Array
function generateCombinations(arr, k) { const n = arr.length; const result = []; let set = (1 << k) - 1; const limit = 1 << n; while (set < limit) { const combination = []; // Extract elements based on set bits for (let i = 0; i < n; i++) { if ((set >> i) & 1) { combination.push(arr[i]); } } result.push(combination); // Next combination const c = set & -set; const r = set + c; set = (((r ^ set) >> 2) / c) | r; } return result;}console.log(generateCombinations(['a', 'b', 'c', 'd'], 2));// [['a','b'], ['a','c'], ['b','c'], ['a','d'], ['b','d'], ['c','d']]
1. Bitmasking for Subset Enumeration
Operation
Technique
Steps
Complexity
Reverse Bits
Swap bits from outside to inside
Swap 16-bit halves, 8-bit, 4-bit, 2-bit, 1-bit
O(log n) bits
Swap Adjacent Bits
Mask odd and even bits separately
((n & 0xAAAAAAAA)>>1)|((n & 0x55555555)<<1)
O(1)
Swap Nibbles
Swap 4-bit groups
((n & 0xF0F0F0F0)>>4)|((n & 0x0F0F0F0F)<<4)
O(1)
Swap Bytes
Swap 8-bit groups
((n & 0xFF00FF00)>>8)|((n & 0x00FF00FF)<<8)
O(1)
Lookup Table
Precompute reverse for 0-255
Split into bytes, lookup each, reassemble
O(1)
Example: Reverse 32-bit Integer
function reverseBits(n) { // Swap 16-bit halves n = ((n & 0xFFFF0000) >>> 16) | ((n & 0x0000FFFF) << 16); // Swap 8-bit halves within each 16-bit group n = ((n & 0xFF00FF00) >>> 8) | ((n & 0x00FF00FF) << 8); // Swap 4-bit nibbles within each 8-bit group n = ((n & 0xF0F0F0F0) >>> 4) | ((n & 0x0F0F0F0F) << 4); // Swap 2-bit pairs within each 4-bit group n = ((n & 0xCCCCCCCC) >>> 2) | ((n & 0x33333333) << 2); // Swap adjacent bits n = ((n & 0xAAAAAAAA) >>> 1) | ((n & 0x55555555) << 1); return n >>> 0; // Convert to unsigned}console.log(reverseBits(43261596).toString(2)); // Input: 00000010100101000001111010011100 (43261596)// Output: 00111001011110000010100101000000 (964176192)
Note: Use >>> (unsigned right shift) instead of >> when working with bit reversal to avoid sign extension issues with negative numbers. The mask 0xAAAAAAAA = all odd bits, 0x55555555 = all even bits.
Summary: Bit Manipulation Pattern
Bitmasking: Use integers as sets - iterate 2n subsets with for (mask=0; mask<(1<<n); mask++)
Subset Generation: Powers of 2 are single bits; iterate submasks with s=(s-1)&mask
Counting Bits: Brian Kernighan's n&(n-1) removes rightmost bit - O(k) for k set bits
XOR Properties:a^a=0, a^0=a - perfect for finding unique elements in duplicates
Bit Tricks:n<<k = multiply by 2k, n>>k = divide by 2k, n&1 = check odd/even
Hamming Distance: Count bits in x^y - for arrays, count per bit position
Power of 2: Check with n>0 && (n&(n-1))===0 - exactly one bit set
Gray Code:binary^(binary>>1) - adjacent codes differ by 1 bit
Gosper's Hack: Generate next k-combination efficiently - maintains bit count