dsa-prep/bit-manipulation.md

16 — Bit Manipulation

1. Binary basics

~6 min read·updated 5/29/2026

16 — Bit Manipulation

Neetcode Week 12c. Bit tricks are niche but pop up at Google often enough to be worth learning. The good news: there are only ~10 idioms, and once you've seen them, they're easy.

Table of Contents

  1. Binary basics
  2. Operators
  3. Common idioms (memorize these)
  4. Pattern: XOR tricks
  5. Pattern: Counting bits
  6. Pattern: Bitmask DP
  7. Pattern: Bitwise on signed integers
  8. Walked-through problems
  9. Pitfalls
  10. Practice list

1. Binary basics

Decimal  Binary       Hex
0        0            0
1        1            1
2        10           2
5        101          5
10       1010         A
15       1111         F
255      11111111     FF

A 32-bit signed integer: leftmost bit is sign (0 = positive, 1 = negative two's-complement).

   sign     value bits
    │     ←  31 bits  →
   [s][b][b]....[b][b][b]
   bit 31              bit 0

Two's complement

To negate a number: flip all bits and add 1.

 5 = 0000 0101
-5 = 1111 1011    (flip and +1)

This is why ~x == -x - 1 in C / Java / Python (the formula matters for bit puzzles).


2. Operators

OpMeaningExample
&AND0b1100 & 0b1010 = 0b1000
|OR0b1100 | 0b1010 = 0b1110
^XOR0b1100 ^ 0b1010 = 0b0110
~NOT~0 = -1 (in 2's complement)
<<left shift1 << 3 = 8
>>right shift8 >> 1 = 4

Truth tables

  a b   AND  OR  XOR
  0 0    0    0   0
  0 1    0    1   1
  1 0    0    1   1
  1 1    1    1   0

XOR is the odd-parity operator: 1 iff inputs differ.


3. Common idioms

Memorize these. Each is one line and unlocks many problems.

IdiomCodeMeaning
Test bit i(x >> i) & 10 or 1
Set bit ix | (1 << i)turn on
Clear bit ix & ~(1 << i)turn off
Toggle bit ix ^ (1 << i)flip
Lowest set bitx & -xisolates lowest 1
Clear lowest set bitx & (x - 1)turns lowest 1 into 0
Test if power of 2x > 0 and (x & (x-1)) == 00...010...0
Count set bitsbin(x).count('1') (Python) or Brian Kernighan looppopcount
Round down to power of 21 << (x.bit_length() - 1)floor
Round up to power of 21 << x.bit_length() if x != power-of-2, else xceil
Swap with XORa^=b; b^=a; a^=bno temp variable

Brian Kernighan's bit count

def count_bits(x): cnt = 0 while x: x &= x - 1 # clear lowest set bit cnt += 1 return cnt

O(number of set bits), often faster than bit-by-bit scan for sparse numbers.

Swap without temp

a ^= b b ^= a a ^= b

(Cute, but slow vs a, b = b, a in Python — interview trivia only.)


4. Pattern: XOR tricks

XOR properties:

  • x ^ x = 0 — anything XORed with itself is 0.
  • x ^ 0 = x — XOR with 0 is identity.
  • XOR is commutative and associative.

These three facts power many "find the unique" problems.

Single Number (Easy)

Every number appears twice except one. Find it.

def single_number(nums): result = 0 for x in nums: result ^= x return result

XOR of all → pairs cancel out, lone element remains. O(n) time, O(1) space.

Single Number II — appears 3 times

Every number appears 3 times except one (which appears once). Find it.

The XOR trick doesn't directly apply (3 isn't even). Generalize: count bits modulo 3.

def single_number_iii(nums): result = 0 for i in range(32): bit_count = sum((x >> i) & 1 for x in nums) if bit_count % 3: result |= 1 << i # Handle negative numbers (Python ints are arbitrary precision) if result >= 2**31: result -= 2**32 return result

Missing Number (Easy)

Array of n distinct numbers from 0..n. Find missing.

def missing_number(nums): res = len(nums) for i, x in enumerate(nums): res ^= i ^ x return res

XOR of 0..n xored with XOR of nums leaves only the missing number.

Two Numbers Appearing Once

Every number appears twice except two — find both.

def single_number_iv(nums): xor_all = 0 for x in nums: xor_all ^= x # = a ^ b (the two unique) diff_bit = xor_all & -xor_all # lowest bit where a and b differ a = b = 0 for x in nums: if x & diff_bit: a ^= x else: b ^= x return [a, b]

Split numbers by a bit where the two unique differ. Each group XORs to one of them.


5. Pattern: Counting bits

Number of 1 Bits (Easy)

def hamming_weight(n): cnt = 0 while n: cnt += n & 1 n >>= 1 return cnt

Or n &= n - 1 trick (only counts set bits).

Counting Bits 0..n (Easy)

Return array [count_of_1s_in_i for i in 0..n].

def count_bits(n): dp = [0] * (n + 1) for i in range(1, n + 1): dp[i] = dp[i >> 1] + (i & 1) # half + lowest bit return dp

dp[i] = dp[i // 2] + i % 2 — the number of bits is the count in i // 2 plus the lowest bit.

Reverse Bits (Easy)

def reverse_bits(n): res = 0 for _ in range(32): res = (res << 1) | (n & 1) n >>= 1 return res

Build the result by shifting left and OR-ing the bottom bit of input.


6. Pattern: Bitmask DP

When n ≤ ~20, you can encode "which subset is selected" in 2ⁿ states.

Travelling Salesman (n ≤ 20) — classic

dp[mask][i] = min cost to visit every city in mask, ending at i.

def tsp(dist, start=0): n = len(dist) INF = float('inf') dp = [[INF] * n for _ in range(1 << n)] dp[1 << start][start] = 0 for mask in range(1 << n): for i in range(n): if not (mask >> i) & 1: continue if dp[mask][i] == INF: continue for j in range(n): if (mask >> j) & 1: continue # j already in mask new_mask = mask | (1 << j) dp[new_mask][j] = min(dp[new_mask][j], dp[mask][i] + dist[i][j]) return min(dp[(1 << n) - 1][i] + dist[i][start] for i in range(n))

Iterate all subsets of a set

n = 4 for mask in range(1 << n): # 2^n subsets subset = [i for i in range(n) if (mask >> i) & 1] print(subset)

Iterate all subsets of a subset

sub = mask while sub > 0: # use sub sub = (sub - 1) & mask

Niche but powerful for partition DP.


7. Pattern: Bitwise on signed integers

In C++/Java, int is 32-bit. Right shift of negative numbers is implementation-defined for >>, defined for >>> (unsigned).

In Python, ints are arbitrary precision. To simulate 32-bit:

n &= 0xFFFFFFFF

Sum of Two Integers (without + or -)

Use bitwise.

def get_sum(a, b): MASK = 0xFFFFFFFF while b & MASK: carry = (a & b) << 1 # carry bits a = a ^ b # sum without carry b = carry return a & MASK if a >= 0 else a

XOR = sum without carry; AND << 1 = carry. Loop until no carry. The MASK is needed in Python for negative numbers (otherwise the loop never terminates).


8. Walked-through problems

Subsets via bitmask

def subsets(nums): n = len(nums) res = [] for mask in range(1 << n): subset = [nums[i] for i in range(n) if (mask >> i) & 1] res.append(subset) return res

Equivalent to backtracking but iterative. O(n · 2ⁿ).

Power of Two (Easy)

def is_power_of_two(n): return n > 0 and (n & (n - 1)) == 0

A power of 2 has exactly one bit set. n & (n-1) clears the lowest bit, leaving 0 only if there was just that one bit.

Counting Set Bits with DP

Already shown above (dp[i] = dp[i >> 1] + (i & 1)).


9. Pitfalls

Operator precedence

x & 1 == 1 is not (x & 1) == 1. In C/Python, == binds tighter than &. Always parenthesize:

((x >> i) & 1) == 1

Or just (x >> i) & 1 (truthy check).

Negative shifts

Right-shifting a negative int in C is implementation-defined for signed >>. Use >>> in Java, or convert to unsigned. In Python, >> of negatives works mathematically but might surprise:

-5 >> 1 # -3 (floor division)

Off-by-one in 1 << n

1 << 32 in C/Java is undefined for 32-bit int. In Python it's fine (arbitrary precision). State your assumed bit width.

Counting all bits is slow

bin(x).count('1') is O(bits) — fine. for i in range(64): if (x >> i) & 1: ... is also O(64). Don't write nested loops over bits unnecessarily.

Confusing ^ with power

In Python ^ is XOR. ** is exponentiation. In math notation ^ is power, but not in code.

Forgetting Python ints are unbounded

x &= 0xFFFFFFFF to truncate to 32 bits when emulating fixed-width arithmetic.


10. Practice list

#ProblemPatternDifficulty
136Single NumberXOREasy
137Single Number IIBit-count mod 3Medium
191Number of 1 BitsPopcountEasy
338Counting BitsDPEasy
190Reverse BitsShift + OREasy
268Missing NumberXOR or sumEasy
7Reverse IntegerMath/bitMedium
371Sum of Two IntegersCarry simMedium
78SubsetsBitmask iterMedium
231Power of Twon & (n-1)Easy

TL;DR ✅

  • XOR cancels pairs. Use it to find a unique element among duplicates.
  • x & (x - 1) clears lowest set bit. x & -x isolates it.
  • (n & (n-1)) == 0 tests power-of-two.
  • Bit DP: dp[i] = dp[i >> 1] + (i & 1) for popcount table.
  • Bitmask DP when n ≤ 20 — encode subsets in 2ⁿ states.
  • Always parenthesize &, |, ^ due to low precedence.
  • Python ints are arbitrary-precision — mask with 0xFFFFFFFF to simulate 32-bit.

Next: 17-patterns-cheatsheet.md.

External resources

// 1 view

main
UTF-8·typescript