Bit Manipulation and Bitwise Operations
1. Bitmasking for Subset Enumeration
| Concept | Technique | Formula/Pattern | Use Case |
|---|---|---|---|
| Subset as Bitmask | Represent subset using binary number | bit i set → element i included |
Enumerate all 2n subsets |
| Iterate All Subsets | Loop from 0 to 2n-1 | for (let mask = 0; mask < (1<<n); mask++) |
Generate powerset efficiently |
| Check Bit Set | Test if bit i is set in mask | (mask >> i) & 1 or mask & (1<<i) |
Determine element membership |
| Set Bit | Turn on bit i | mask | (1 << i) |
Add element to subset |
| Clear Bit | Turn off bit i | mask & ~(1 << i) |
Remove element from subset |
| Toggle Bit | Flip bit i | mask ^ (1 << i) |
Add/remove element toggle |
Example: Generate All Subsets Using Bitmask
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 bit
function 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 counting
function 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 2
function isPowerOfTwo(n) {
return n > 0 && (n & (n - 1)) === 0;
}
// Get next power of 2
function 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 variable
function 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 signs
function oppositeSign(a, b) {
return (a ^ b) < 0;
}
console.log(isPowerOfTwo(16)); // true
console.log(nextPowerOfTwo(10)); // 16
swap(5, 10); // Before: a=5, b=10, After: a=10, b=5
console.log(oppositeSign(5, -3)); // true
console.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)); // 16
console.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 4
console.log(missingPowerOfTwo([1, 2, 8, 16, 32], 5)); // 4
8. Gray Code Generation Algorithm
| Concept | Property | Formula | Use Case |
|---|---|---|---|
| Gray Code | Adjacent codes differ by exactly 1 bit | gray = binary ^ (binary >> 1) |
Error correction, rotary encoders |
| Binary to Gray | XOR with right-shifted version | g = b ^ (b >> 1) |
Convert standard binary to Gray |
| Gray to Binary | Accumulate XOR from MSB | b ^= g >> i for all i |
Decode Gray code to binary |
| N-bit Gray Codes | Recursive: reflect previous, prepend 0/1 | 2n unique codes | Generate all n-bit Gray codes |
Example: Generate Gray Code Sequence
function grayCode(n) {
const result = [];
const size = 1 << n; // 2^n
for (let i = 0; i < size; i++) {
// Convert binary i to Gray code
result.push(i ^ (i >> 1));
}
return result;
}
console.log(grayCode(2)); // [0, 1, 3, 2]
// Binary: 00, 01, 10, 11
// Gray: 00, 01, 11, 10
//
// 0 ^ (0 >> 1) = 0 ^ 0 = 0 (00)
// 1 ^ (1 >> 1) = 1 ^ 0 = 1 (01)
// 2 ^ (2 >> 1) = 2 ^ 1 = 3 (11)
// 3 ^ (3 >> 1) = 3 ^ 1 = 2 (10)
console.log(grayCode(3)); // [0,1,3,2,6,7,5,4]
// Verify: each adjacent pair differs by 1 bit
Example: Gray Code Conversion
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 method
function 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 bits
console.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 bits
function 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)
Example: Bit Reversal Variants
// Simple bit-by-bit reversal
function reverseBitsSimple(n) {
let result = 0;
for (let i = 0; i < 32; i++) {
result <<= 1;
result |= (n & 1);
n >>>= 1;
}
return result >>> 0;
}
// Swap adjacent bits
function swapAdjacentBits(n) {
// 0xAAAAAAAA = 10101010... (odd positions)
// 0x55555555 = 01010101... (even positions)
return (((n & 0xAAAAAAAA) >>> 1) | ((n & 0x55555555) << 1)) >>> 0;
}
// Swap nibbles (4-bit groups)
function swapNibbles(n) {
// 0xF0F0F0F0 = high nibbles, 0x0F0F0F0F = low nibbles
return (((n & 0xF0F0F0F0) >>> 4) | ((n & 0x0F0F0F0F) << 4)) >>> 0;
}
// Reverse byte order (big-endian ↔ little-endian)
function reverseBytes(n) {
return (((n & 0xFF000000) >>> 24) |
((n & 0x00FF0000) >>> 8) |
((n & 0x0000FF00) << 8) |
((n & 0x000000FF) << 24)) >>> 0;
}
console.log(reverseBitsSimple(5)); // Same as reverseBits
console.log(swapAdjacentBits(0b10110100)); // 0b01101000
console.log(swapNibbles(0x12345678).toString(16)); // 0x21436587
console.log(reverseBytes(0x12345678).toString(16)); // 0x78563412
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
- Reverse Bits: Swap progressively smaller chunks - 16-bit, 8-bit, 4-bit, 2-bit, 1-bit
- Key Insight: Bits can represent sets, states, and choices - enables compact DP and combinatorial algorithms