Bit ManipulationXORBitmaskAND / OR / NOTBit ShiftingO(1) Tricks

Bit Manipulation — The Complete Guide

Master bit manipulation from first principles. Learn XOR tricks, bitmask techniques, and bit-level operations to solve problems in O(1) space and impress interviewers with elegant solutions.

40 min read10 sections
01

Intuition from Scratch

Why does this pattern exist?

Every number in a computer is stored as a sequence of bits — 0s and 1s. The number 5 is 101 in binary, 13 is 1101. Bit manipulation means operating directly on these individual bits using special operators: AND, OR, XOR, NOT, and shifts.

Why bother? Because bit operations are the fastest operations a CPU can perform — they happen in a single clock cycle. And certain problems have incredibly elegant solutions when you think at the bit level. Finding a unique number among duplicates? One line with XOR. Checking if a number is a power of 2? One line with AND. Generating all subsets? A loop with bitmasks.

Bit manipulation isn't about memorizing tricks — it's about understanding what each operator does to individual bits, and then recognizing when a problem's structure maps to bit-level operations.

The Operators — Your Toolkit

Bitwise Operatorstext
AND (&)  — Both bits must be 11. Otherwise0.
           1010 & 1100 = 1000
           Use: Check if a bit is set, clear bits, mask bits

OR  (|)  — At least one bit is 11. Both 00.
           1010 | 1100 = 1110
           Use: Set a bit, combine flags

XOR (^)  — Bits are different1. Same0.
           1010 ^ 1100 = 0110
           Use: Find unique element, toggle bits, swap without temp

NOT (~)  — Flip every bit. 01, 10.
           ~1010 = 0101 (simplified; actual result depends on bit width)
           Use: Invert a mask

LEFT SHIFT (<<)  — Shift bits left, fill with 0s. Equivalent to ×2.
                    0011 << 1 = 0110 (36)
                    Use: Multiply by powers of 2, create bit masks

RIGHT SHIFT (>>) — Shift bits right, drop rightmost. Equivalent to ÷2.
                    0110 >> 1 = 0011 (63)
                    Use: Divide by powers of 2, check individual bits

Analogies to Build Intuition

💡

Light Switches on a Panel

Imagine a row of light switches (bits). AND checks if BOTH switches are on. OR checks if EITHER is on. XOR checks if they're in DIFFERENT positions. NOT flips every switch. Bit manipulation is just flipping, checking, and combining switches.

🎭

XOR as a 'Cancel Out' Operator

XOR is like a toggle: applying it twice cancels out. If you XOR a number with itself, you get 0 (it cancels). If you XOR 0 with any number, you get that number back. This is why XOR finds the unique element — all duplicates cancel each other out.

📋

Bitmask as a Checklist

A bitmask is a number where each bit represents a yes/no flag. Bit 0 = 'visited city A?', bit 1 = 'visited city B?', etc. The number 5 (101) means 'visited A and C but not B.' Bitmasks let you represent sets of items as a single integer.

Essential Bit Tricks — Memorize These

Must-Know Bit Trickstypescript
// Check if bit i is set (1)
(n >> i) & 1            // or: n & (1 << i)

// Set bit i to 1
n | (1 << i)

// Clear bit i (set to 0)
n & ~(1 << i)

// Toggle bit i
n ^ (1 << i)

// Check if n is a power of 2
n > 0 && (n & (n - 1)) === 0

// Get the lowest set bit (rightmost 1)
n & (-n)               // or: n & ~(n - 1)

// Clear the lowest set bit
n & (n - 1)

// Count set bits (Brian Kernighan's)
let count = 0;
while (n) { n &= (n - 1); count++; }

// Check if n is even/odd
(n & 1) === 0  // even
(n & 1) === 1  // odd

The core insight

Bit manipulation gives you O(1) operations on individual bits within a number. XOR cancels duplicates. AND masks and checks. OR sets flags. n & (n-1) clears the lowest set bit. These primitives combine to solve problems that would otherwise need hash maps, sorting, or extra space.

02

Pattern Recognition

Bit manipulation problems rarely say "use bits." They hide behind constraints like "O(1) space" or "without extra memory." Here are the signals.

Keywords & Signals in the Problem Statement

Use Bit Manipulation when you see...

  • "Every element appears twice except one" → XOR
  • "O(1) extra space" on a duplicate/missing number problem
  • "Power of 2" or "power of 4"
  • "Count the number of 1 bits" (Hamming weight)
  • "Hamming distance" between two numbers
  • "Generate all subsets" of a set with n ≤ 20
  • "Swap two numbers without a temp variable"
  • "Reverse bits" of an integer
  • "Single number" among duplicates
  • Constraints mention 32-bit integers or bit-level operations

Don't use Bit Manipulation when...

  • The problem involves floating-point numbers (bits don't map cleanly)
  • You need to find pairs with a specific sum (use two pointers or hashing)
  • The problem is about string manipulation (usually not bit-related)
  • n is large (> 20-25) and you need bitmask DP (2^n states explode)
  • The problem involves sorting or ordering (bits don't help with order)
  • A simpler approach (hash map, math) is more readable and equally fast

Bit Manipulation vs. Similar Approaches

ApproachWhen to UseKey Difference
XOR trickFind unique element among duplicates, O(1) spaceXOR cancels pairs: a ^ a = 0. No extra memory needed.
Hash MapCount frequencies, find duplicates, general lookupsO(n) space. More flexible but uses extra memory.
Bitmask (subset enumeration)Generate all subsets, bitmask DP (n ≤ 20)Represent sets as integers. 2^n states. Compact and fast.
BacktrackingGenerate subsets/combinations for larger nMore flexible, handles constraints. Bitmask is faster for small n.
Math tricksMissing number (sum formula), power checksSometimes simpler than bits. Use whichever is cleaner.

The mental test

Ask yourself: "Does this problem involve finding something unique among duplicates? Checking properties of binary representation? Representing subsets compactly? Operating without extra space?" If yes, think bits. The strongest signal is "O(1) space" combined with duplicate/missing number problems.

03

Brute Force → Optimized Thinking

Let's walk through the thinking evolution using a classic example: Single Number — every element in an array appears twice except one. Find the unique element.

1

Brute Force — Nested Loop

O(n²) time · O(1) space

For each element, scan the entire array to see if it appears again. If not, it's the unique one. This works but is painfully slow.

Brute Forcetypescript
function singleNumber(nums: number[]): number {
  for (let i = 0; i < nums.length; i++) {
    let found = false;
    for (let j = 0; j < nums.length; j++) {
      if (i !== j && nums[i] === nums[j]) {
        found = true;
        break;
      }
    }
    if (!found) return nums[i];
  }
  return -1;
}
// O(n²) time, O(1) space. Too slow for large inputs.
2

Better — Hash Map

O(n) time · O(n) space

Count frequencies with a hash map. The element with count 1 is the answer. Fast, but uses O(n) extra space.

Hash Map Approachtypescript
function singleNumber(nums: number[]): number {
  const freq = new Map<number, number>();
  for (const n of nums) {
    freq.set(n, (freq.get(n) ?? 0) + 1);
  }
  for (const [num, count] of freq) {
    if (count === 1) return num;
  }
  return -1;
}
// O(n) time, O(n) space. Can we do O(1) space?
3

Optimal — XOR Everything

O(n) time · O(1) space

XOR has a magical property: a ^ a = 0 and a ^ 0 = a. If you XOR all elements together, every pair cancels out (becomes 0), and only the unique element remains. One pass, no extra memory.

XOR — Optimaltypescript
function singleNumber(nums: number[]): number {
  let result = 0;
  for (const n of nums) {
    result ^= n;
  }
  return result;
}
// [4, 1, 2, 1, 2] → 4^1^2^1^2 = (1^1)^(2^2)^4 = 0^0^4 = 4
// O(n) time, O(1) space. Perfect.

Why Does the Optimization Work?

1

XOR Properties That Make This Work

Three properties: (1) a ^ a = 0 (self-cancellation). (2) a ^ 0 = a (identity). (3) XOR is commutative and associative (order doesn't matter). Together: XOR-ing all elements cancels every pair, leaving only the unique one.

2

From Hash Map to XOR

The hash map tracks 'which elements have been seen.' XOR does the same thing implicitly — it 'remembers' elements that have appeared an odd number of times and 'forgets' elements that have appeared an even number of times. It's a 1-bit frequency counter per bit position.

3

The General Principle

Bit manipulation optimizations work when the problem's structure aligns with bit-level operations. 'Find the unique among pairs' maps perfectly to XOR's cancellation property. Not every problem has a bit solution — but when it does, it's usually the most elegant one.

The thinking process matters more than the code

In interviews, explain: "XOR has the property that a ^ a = 0 and a ^ 0 = a. If I XOR all elements, every duplicate pair cancels to 0, and only the unique element survives. This gives me O(n) time and O(1) space." The interviewer wants to see you understand WHY it works.

04

Core Templates

Bit manipulation problems fall into a few reusable patterns. Memorize these templates — they cover the vast majority of interview questions.

Template 1: XOR to Find Unique Elements

XOR all elements. Pairs cancel out, leaving the unique one(s). Works when every element appears an even number of times except the target.

XOR Unique Element Templatetypescript
// Single unique element (all others appear twice)
function findUnique(nums: number[]): number {
  let xor = 0;
  for (const n of nums) xor ^= n;
  return xor;
}

// TWO unique elements (all others appear twice)
function findTwoUnique(nums: number[]): [number, number] {
  // Step 1: XOR all → gives xor of the two unique numbers
  let xor = 0;
  for (const n of nums) xor ^= n;

  // Step 2: Find any set bit (the two numbers differ here)
  const diffBit = xor & (-xor); // lowest set bit

  // Step 3: Split into two groups by that bit, XOR each group
  let a = 0, b = 0;
  for (const n of nums) {
    if (n & diffBit) a ^= n;
    else b ^= n;
  }

  return [a, b];
}
// Why this works: the two unique numbers differ in at least one bit.
// Splitting by that bit puts them in different groups.
// Within each group, all pairs still cancel, leaving one unique each.

Template 2: Bit Counting (Brian Kernighan's)

Count the number of set bits (1s) in a number. The trick: n & (n-1) clears the lowest set bit. Repeat until n is 0.

Bit Counting Templatetypescript
// Count set bits (Hamming weight)
function countBits(n: number): number {
  let count = 0;
  while (n) {
    n &= (n - 1); // clear lowest set bit
    count++;
  }
  return count;
}
// Why n & (n-1) clears the lowest set bit:
// n     = ...1000  (lowest set bit is at position 3)
// n-1   = ...0111  (borrows from that bit, flips everything below)
// n&n-1 = ...0000  (the lowest set bit is gone)
//
// This runs in O(k) where k = number of set bits, not O(32).

// Hamming distance between two numbers
function hammingDistance(x: number, y: number): number {
  return countBits(x ^ y);
  // XOR gives 1s exactly where x and y differ
}

Template 3: Power of 2 Check

A power of 2 has exactly one set bit: 1, 10, 100, 1000... So n & (n-1) === 0 (clearing the only set bit gives 0).

Power of 2 Templatetypescript
function isPowerOfTwo(n: number): boolean {
  return n > 0 && (n & (n - 1)) === 0;
}
// 8 = 1000, 7 = 0111 → 1000 & 0111 = 0000 → true
// 6 = 0110, 5 = 0101 → 0110 & 0101 = 0100 → false (not power of 2)

// Power of 4: power of 2 AND set bit is at an even position
function isPowerOfFour(n: number): boolean {
  return n > 0 && (n & (n - 1)) === 0 && (n & 0x55555555) !== 0;
  // 0x55555555 = 01010101... (bits at even positions)
}

Template 4: Bitmask Subset Enumeration

Use an integer as a bitmask where bit i represents whether element i is included. Iterate from 0 to 2^n - 1 to generate all subsets.

Bitmask Subset Templatetypescript
function subsets(nums: number[]): number[][] {
  const n = nums.length;
  const result: number[][] = [];

  for (let mask = 0; mask < (1 << n); mask++) {
    const subset: number[] = [];
    for (let i = 0; i < n; i++) {
      if (mask & (1 << i)) { // bit i is set → include nums[i]
        subset.push(nums[i]);
      }
    }
    result.push(subset);
  }

  return result;
}
// For nums = [1, 2, 3]:
// mask=0 (000) → []
// mask=1 (001) → [1]
// mask=2 (010) → [2]
// mask=3 (011) → [1, 2]
// mask=4 (100) → [3]
// mask=5 (101) → [1, 3]
// mask=6 (110) → [2, 3]
// mask=7 (111) → [1, 2, 3]
// Only works for n ≤ ~20 (2^20 ≈ 1 million states)

Template 5: Bit-by-Bit Construction

Build the answer one bit at a time, from the most significant bit to the least (or vice versa). Used for problems like Maximum XOR, Reverse Bits.

Bit-by-Bit Templatetypescript
// Reverse bits of a 32-bit integer
function reverseBits(n: number): number {
  let result = 0;
  for (let i = 0; i < 32; i++) {
    result = (result << 1) | (n & 1); // shift result left, add lowest bit of n
    n >>>= 1;                          // shift n right (unsigned)
  }
  return result >>> 0; // convert to unsigned 32-bit
}
// Process one bit at a time: extract from n, place into result.
// This pattern works for any "transform bits" problem.

Which template to pick?

Find unique among duplicates? → Template 1 (XOR). Count bits or Hamming distance? → Template 2 (Kernighan's). Power of 2/4 check? → Template 3 (n & (n-1)). Generate all subsets (n ≤ 20)? → Template 4 (bitmask). Transform or reverse bits? → Template 5 (bit-by-bit).

05

Variations & Sub-Patterns

The templates cover the basics, but real interview problems add twists. Here are the most important variations.

VariationCore TechniqueClassic Example
Single unique (pairs)XOR all elements → unique remainsSingle Number (LC 136)
Two unique (pairs)XOR all → split by differing bit → XOR each groupSingle Number III (LC 260)
Unique among tripletsCount bits mod 3 at each positionSingle Number II (LC 137)
Count set bitsBrian Kernighan's: n &= (n-1) until 0Number of 1 Bits (LC 191)
Counting bits for rangeDP: countBits[i] = countBits[i >> 1] + (i & 1)Counting Bits (LC 338)
Power checksn & (n-1) === 0 for power of 2; add mask for power of 4Power of Two (LC 231), Power of Four (LC 342)
Reverse / rotate bitsBit-by-bit extraction and placementReverse Bits (LC 190)
Missing numberXOR indices and values — missing one doesn't cancelMissing Number (LC 268)
Bitmask DPState = bitmask of visited items, transition = add/remove itemPartition to K Equal Sum Subsets (LC 698)
Subset enumerationLoop 0 to 2^n - 1, check each bit for inclusionSubsets (LC 78)

Deep Dive: Single Number II (Every Element Appears Three Times)

XOR alone doesn't work here because a ^ a ^ a = a (odd count doesn't cancel). Instead, count the number of set bits at each position across all numbers. If the count mod 3 is not 0, that bit belongs to the unique number.

Problems mentioned above

Single Number II — Bit Counting Mod 3typescript
function singleNumber(nums: number[]): number {
  let result = 0;

  for (let bit = 0; bit < 32; bit++) {
    let sum = 0;
    for (const n of nums) {
      sum += (n >> bit) & 1; // count how many numbers have this bit set
    }
    if (sum % 3 !== 0) {
      result |= (1 << bit); // this bit belongs to the unique number
    }
  }

  return result;
}
// For each of the 32 bit positions, count how many numbers have a 1.
// If count % 3 ≠ 0, the unique number has a 1 at that position.
// This generalizes: for "appears k times except one", use count % k.

Deep Dive: Missing Number (XOR with Indices)

Array contains n numbers from 0 to n with one missing. XOR all array values with all indices 0 to n. Every number that's present cancels with its index. The missing number is left.

Missing Number — XOR Approachtypescript
function missingNumber(nums: number[]): number {
  let xor = nums.length; // start with n (the last index)

  for (let i = 0; i < nums.length; i++) {
    xor ^= i ^ nums[i]; // XOR index and value
  }

  return xor;
}
// nums = [3, 0, 1], n = 3
// xor = 3 ^ (0^3) ^ (1^0) ^ (2^1)
//     = 3 ^ 3 ^ 0 ^ 1 ^ 0 ^ 2 ^ 1
//     = (3^3) ^ (0^0) ^ (1^1) ^ 2
//     = 0 ^ 0 ^ 0 ^ 2 = 2 ← the missing number

Generalizing the XOR trick

XOR works for "find the odd one out" whenever elements appear in pairs (or any even count). For triplets, use bit counting mod 3. For two unique elements, split by a differing bit. The core idea is always the same: exploit cancellation properties.

06

Visual Walkthrough

Let's trace through key bit operations to build muscle memory for how bits behave.

XOR Single Number — Step by Step

XOR Trace — Single Numbertext
Input: [4, 1, 2, 1, 2]

Binary representations:
  4 = 100
  1 = 001
  2 = 010

XOR accumulation:
  result = 000 (start with 0)
  result ^= 4:  000 ^ 100 = 100
  result ^= 1:  100 ^ 001 = 101
  result ^= 2:  101 ^ 010 = 111
  result ^= 1:  111 ^ 001 = 110the 1 cancels
  result ^= 2:  110 ^ 010 = 100the 2 cancels

  result = 100 = 4

Key: XOR is commutative, so order doesn't matter.
Pairs cancel: 1^1 = 0, 2^2 = 0. Only 4 remains.

n & (n-1) — Clearing the Lowest Set Bit

n & (n-1) Tracetext
Why does n & (n-1) clear the lowest set bit?

Example: n = 12 = 1100

n     = 1100
n - 1 = 1011  (subtracting 1 flips the lowest set bit and all bits below it)
n & (n-1) = 1000  (the lowest set bit at position 2 is cleared!)

Another example: n = 10 = 1010
n     = 1010
n - 1 = 1001
n & (n-1) = 1000  ✓ (lowest set bit at position 1 cleared)

Power of 2 check: n = 8 = 1000
n     = 1000
n - 1 = 0111
n & (n-1) = 0000equals 0IS a power of 2

Non-power: n = 6 = 0110
n     = 0110
n - 1 = 0101
n & (n-1) = 0100NOT 0NOT a power of 2

Brian Kernighan's bit counting:
  n = 13 = 1101
  Iteration 1: 1101 & 1100 = 1100 (count=1)
  Iteration 2: 1100 & 1011 = 1000 (count=2)
  Iteration 3: 1000 & 0111 = 0000 (count=3)
  n = 0, stop. Answer: 3 set bits

Bitmask Subset Enumeration — Step by Step

Bitmask Subsets Tracetext
nums = [a, b, c]  (n = 3, so 2^3 = 8 subsets)

mask  binary  bits set    subset
────  ──────  ─────────   ──────
  0    000    none        []
  1    001    bit 0       [a]
  2    010    bit 1       [b]
  3    011    bits 0,1    [a, b]
  4    100    bit 2       [c]
  5    101    bits 0,2    [a, c]
  6    110    bits 1,2    [b, c]
  7    111    bits 0,1,2  [a, b, c]

For each mask, check each bit:
  mask = 5 (101):
    bit 0: 101 & 001 = 0010include a
    bit 1: 101 & 010 = 000 = 0skip b
    bit 2: 101 & 100 = 1000include c
subset = [a, c] ✓

This is equivalent to backtracking but uses integer arithmetic.
Faster for small n (≤ 20) because no recursion overhead.
07

Practice Problems

These 7 problems are carefully ordered from easy to hard. Each one teaches a different bit manipulation technique. Solve them in order.

1

LC 136. Single Number

Easy

Why this pattern:

The purest XOR problem. Every element appears twice except one. XOR all elements — pairs cancel, unique remains. This is the foundation for all XOR-based problems.

Key idea: result = 0; for each n: result ^= n. The answer is result. One line of logic. Understand WHY it works: a ^ a = 0, a ^ 0 = a, XOR is commutative and associative.

2

LC 191. Number of 1 Bits (Hamming Weight)

Easy

Why this pattern:

Bit counting with Brian Kernighan's trick. n &= (n-1) clears the lowest set bit. Count how many times you can do this before n becomes 0.

Key idea: While n ≠ 0: n &= (n-1), count++. This runs in O(k) where k = number of set bits, not O(32). Understand why n & (n-1) clears the lowest set bit — it's the most important bit trick.

3

LC 338. Counting Bits

Easy

Why this pattern:

DP using the bit trick. For each number i from 0 to n, count its set bits. The recurrence: countBits[i] = countBits[i >> 1] + (i & 1). Shifting right removes the last bit; (i & 1) checks if it was set.

Key idea: dp[i] = dp[i >> 1] + (i & 1). The number of set bits in i = set bits in i/2 (shift right) + whether the last bit was 1. This gives O(n) time instead of O(n × 32).

4

LC 268. Missing Number

Easy

Why this pattern:

XOR with indices. Array has n numbers from 0 to n, one missing. XOR all values with all indices 0 to n. Present numbers cancel with their index. The missing one doesn't cancel.

Key idea: xor = n (start with the last index). For each i: xor ^= i ^ nums[i]. Every present number cancels with its index. The missing number has no partner → it survives. Alternative: sum formula (n×(n+1)/2 - sum).

5

LC 190. Reverse Bits

Easy

Why this pattern:

Bit-by-bit construction (Template 5). Extract the lowest bit of n, place it into the result from the other end. Repeat 32 times.

Key idea: For 32 iterations: result = (result << 1) | (n & 1); n >>>= 1. Each step takes the rightmost bit of n and appends it to result. After 32 steps, result has all bits reversed.

6

LC 260. Single Number III

Medium

Why this pattern:

Two unique elements among pairs. XOR all → get XOR of the two unique numbers. Find a differing bit (lowest set bit). Split all numbers into two groups by that bit. XOR each group → one unique per group.

Key idea: Step 1: xor = XOR of all. Step 2: diffBit = xor & (-xor). Step 3: Split and XOR each group. The differing bit guarantees the two unique numbers land in different groups. Pairs still cancel within each group.

7

LC 137. Single Number II

Medium

Why this pattern:

Every element appears three times except one. XOR alone doesn't work (a^a^a = a). Instead, count set bits at each position mod 3. If count % 3 ≠ 0, that bit belongs to the unique number.

Key idea: For each of 32 bit positions: count how many numbers have that bit set. If count % 3 ≠ 0, set that bit in the result. This generalizes to 'appears k times' by using count % k.

Practice strategy

For each problem: (1) Try the brute force first (hash map or nested loop). (2) Ask: "Can I use XOR cancellation? Bit counting? Bitmask?" (3) Identify which template applies. (4) After solving, write the key bit property that makes it work.

08

Common Mistakes

These are the traps that catch people on bit manipulation problems. Learn them here so you don't learn them in an interview.

🔢

Confusing signed and unsigned right shift

In JavaScript/TypeScript, >> is signed (preserves the sign bit) and >>> is unsigned (fills with 0). Using >> on negative numbers gives unexpected results because the sign bit propagates.

For bit manipulation problems, use >>> (unsigned right shift) when working with 32-bit integers. Especially important in Reverse Bits and Number of 1 Bits. Also use >>> 0 to convert to unsigned 32-bit.

Forgetting that XOR only works for pairs

You try to use XOR to find a unique element when others appear three times. a ^ a ^ a = a, not 0. XOR only cancels elements that appear an EVEN number of times.

XOR works when duplicates appear in pairs (2, 4, 6... times). For triplets, use bit counting mod 3. For k copies, use bit counting mod k. Always check: does the duplicate count cancel with XOR?

📐

Off-by-one in bit positions

You use (1 << 32) in JavaScript, which overflows because JS bitwise operations work on 32-bit signed integers. 1 << 31 gives -2147483648 (the sign bit), not 2^31.

JavaScript bitwise operations are 32-bit signed. The valid range for shifts is 0-31. For bit 31, be careful with the sign. Use >>> 0 to convert to unsigned when needed.

🎭

Using bitmask DP when n is too large

You try bitmask DP with n = 25, creating 2^25 ≈ 33 million states. This is too slow and uses too much memory. Bitmask DP is practical only for n ≤ 20 (about 1 million states).

Check the constraint on n. If n ≤ 15-20, bitmask DP is fine. If n > 20, use backtracking with pruning, or find a different approach entirely. 2^20 ≈ 1M is the practical limit.

🔀

Operator precedence surprises

You write if (n & 1 === 0) expecting to check if n is even. But === has higher precedence than &, so it evaluates as n & (1 === 0) = n & false = n & 0 = 0. Always false.

ALWAYS use parentheses with bitwise operators: if ((n & 1) === 0). Bitwise operators have lower precedence than comparison operators in most languages. This is the #1 bit manipulation bug.

🧮

Not handling negative numbers or zero

You check isPowerOfTwo(0) and get true because 0 & (0-1) = 0 & (-1) = 0. But 0 is not a power of 2. Or you forget that negative numbers can't be powers of 2.

Always add n > 0 to power-of-2 checks: n > 0 && (n & (n-1)) === 0. Zero and negative numbers are never powers of 2. Test your solution with 0, -1, and 1.

09

Interview Strategy

Bit manipulation problems test whether you understand low-level operations and can think creatively. Here's how to approach them.

The 5-Step Interview Flow

1

Start with the Obvious Approach

'I can solve this with a hash map in O(n) time and O(n) space.' — Always state the straightforward solution first. This shows you can solve the problem even without bit tricks.

2

Mention the Space Constraint

'But the problem asks for O(1) space. That makes me think of bit manipulation — specifically XOR, since it can find unique elements without extra memory.' — Connect the constraint to the technique.

3

Explain the Bit Property

'XOR has the property that a ^ a = 0 and a ^ 0 = a. If I XOR all elements, pairs cancel out and only the unique element remains.' — State the specific property you're using and WHY it works.

4

Code It Clean

Bit manipulation code is naturally concise. Use parentheses around bitwise operations. Add a comment for the key trick (e.g., '// n & (n-1) clears lowest set bit'). Keep it readable.

5

Test with Binary Examples

Trace through with small numbers in binary: '4 = 100, 1 = 001, 2 = 010. XOR: 100 ^ 001 = 101, 101 ^ 010 = 111, 111 ^ 001 = 110, 110 ^ 010 = 100 = 4.' Binary traces make bit operations concrete.

Common Follow-Up Questions

Follow-UpHow to Handle
"What if elements appear three times instead of two?"XOR doesn't work for odd counts. Use bit counting: for each of 32 positions, count set bits mod 3. Non-zero remainder → that bit belongs to the unique number.
"What if there are TWO unique elements?"XOR all → get XOR of the two uniques. Find a differing bit (lowest set bit). Split numbers into two groups by that bit. XOR each group → one unique per group.
"Can you do it without XOR?"Yes — use sum formula (for missing number), hash map (for single number), or sorting. But XOR gives O(1) space. Mention the trade-offs.
"What about overflow?"In JavaScript, bitwise operations are 32-bit signed. Use >>> 0 to convert to unsigned. In Java/C++, be aware of integer overflow with left shifts. Mention language-specific behavior.
"How would you generate all subsets?"Two approaches: backtracking (flexible, any n) or bitmask enumeration (faster for n ≤ 20). Bitmask: loop 0 to 2^n - 1, check each bit for inclusion. Show you know both.
"What's the time complexity of Brian Kernighan's?"O(k) where k = number of set bits, not O(32). Each iteration clears exactly one set bit. For a number with 3 set bits, it runs 3 times. Worst case is O(32) = O(1) for 32-bit integers.

The meta-skill

Bit manipulation problems are about recognizing when a problem's structure maps to bit-level operations. The interviewer isn't testing if you've memorized tricks — they're testing if you understand WHY the tricks work. Always explain the underlying property (XOR cancellation, n & (n-1) clearing lowest bit, bitmask as set representation) before writing code.

10

Summary & Cheat Sheet

Pin this section. Review it before mock interviews and contests.

Quick Revision Cheat Sheet

XOR properties: a ^ a = 0, a ^ 0 = a, commutative & associative. Cancels pairs → finds unique

n & (n-1): Clears the lowest set bit. Power of 2 check: n > 0 && (n & (n-1)) === 0

n & (-n): Isolates the lowest set bit. Useful for splitting in Single Number III

Brian Kernighan: Count set bits: while (n) { n &= (n-1); count++; } — O(k) where k = set bits

Check bit i: (n >> i) & 1 — returns 1 if bit i is set, 0 otherwise

Set bit i: n | (1 << i) — forces bit i to 1

Clear bit i: n & ~(1 << i) — forces bit i to 0

Toggle bit i: n ^ (1 << i) — flips bit i

Bitmask subsets: Loop mask = 0 to (1 << n) - 1. Check bit i: mask & (1 << i). Only for n ≤ 20

Triplet unique: Count set bits at each position mod 3. Non-zero → bit belongs to unique number

Operator precedence: ALWAYS parenthesize: (n & 1) === 0, not n & 1 === 0. Bitwise < comparison in precedence

Mental trigger: "O(1) space" + "unique among duplicates" → XOR. "Power of 2" → n & (n-1). "All subsets" + n ≤ 20 → bitmask

Decision Flowchart

Which Bit Technique? — Decision Treetext
What does the problem ask?
├── Find unique element among duplicates?
│   ├── Others appear TWICEXOR all elements
│   ├── Others appear THREE timesbit count mod 3 at each position
│   ├── TWO unique elementsXOR all, split by differing bit, XOR groups
│   └── Missing number in 0..nXOR values with indices
├── Count or check bits?
│   ├── Count set bitsBrian Kernighan's: n &= (n-1), count++
│   ├── Power of 2n > 0 && (n & (n-1)) === 0
│   ├── Power of 4power of 2 AND bit at even position
│   ├── Hamming distancecountBits(x ^ y)
│   └── Counting bits 0..nDP: dp[i] = dp[i>>1] + (i&1)
├── Transform bits?
│   ├── Reverse bitsbit-by-bit: extract lowest, place in result
│   ├── Set/clear/toggle bit iuse masks: | (1<<i), & ~(1<<i), ^ (1<<i)
│   └── Swap without tempa ^= b; b ^= a; a ^= b
├── Generate all subsets (n20)?
│   → Bitmask: loop 0 to 2^n - 1, check each bit
└── Not sure?
Start with hash map / math. If O(1) space is required, think bits.

One last thing

Bit manipulation is the most "trick-based" pattern in DSA. But the tricks aren't random — they all follow from a few core properties: XOR cancellation, n & (n-1) clearing the lowest bit, and bitmasks representing sets. Understand these three ideas deeply, solve the 7 problems in Section 07, and you'll handle any bit manipulation question with confidence. When in doubt, trace through the binary representation on paper — bits become intuitive when you can see them.