Pattern 20 · Bit Tricks

Bit Manipulation

Treat an integer as a row of on/off switches instead of a quantity. A handful of operators — XOR, AND, shifts — turn "find the number that appears once" and "count the set bits" into one clean pass with no extra memory.

time O(n) · O(1) per opspace O(1)core tricks XOR · n & (n−1)practice 14 problems
01 core concept

A number is a row of switches

Everything in this pattern comes from one shift in perspective: stop reading 12 as a quantity and start reading it as 1100 — four columns, each either on or off. Bitwise operators work on every column at once, independently, in a single CPU instruction. That is why these solutions come out O(1) extra space with no hash set in sight.

opreads ascolumn ruleyou reach for it when…
a & bAND1 only if both are 1testing or clearing bits, masking
a | bOR1 if either is 1turning bits on, merging masks
a ^ bXOR1 if they differtoggling, cancelling pairs, "find the odd one"
~aNOTflip every columnbuilding the complement of a mask
a << kshift leftslide up k columnsmultiply by 2k, build 1 << i masks
a >> kshift rightslide down k columnsdivide by 2k, walk the bits

XORThe four facts worth memorising

  • x ^ x == 0 — anything cancels itself. This is the entire Single Number family.
  • x ^ 0 == x — zero is the identity, so it is always the safe starting accumulator.
  • x ^ y ^ y == x — XOR is its own inverse; applying it twice undoes it.
  • XOR is commutative and associative, so the order of a whole array of XORs never matters.

Put those together and "one number appears once, the rest appear twice" stops being a counting problem. Fold the array into a single accumulator and the duplicates delete each other on the way past.

1 / 8walkthrough — XOR cancels the pairs (LC 136)
08s
04s
02s
01s
nums [4, 1, 2, 1, 2]
running xor 0
Goal: find the one number in [4, 1, 2, 1, 2] that has no partner. The four cells are the bits of our running answer, written most-significant first: the 8s column, the 4s column, the 2s, the 1s. It starts at 0000 — plain zero.
02 the toolkit

Five moves that cover almost every problem

M1n & (n - 1) — clear the lowest set bit

Subtracting 1 borrows through the trailing zeros: the lowest 1 turns into a 0 and every 0 below it turns into a 1, while everything above is left alone. ANDing the two therefore keeps the untouched top and wipes the tail — exactly one set bit disappears.

n = 1 0 0 1 1 1 0 0 (156) n - 1 = 1 0 0 1 1 0 1 1 (155) lowest 1 off, zeros below flipped on ------------------------ & result= 1 0 0 1 1 0 0 0 (152) one fewer set bit

Two consequences you will use constantly: looping n = n & (n - 1) until n is zero counts the set bits in O(number of 1s) instead of O(32), and a number with exactly one set bit satisfies n & (n - 1) == 0 — the power-of-two test.

1 / 9walkthrough — counting set bits with n & (n − 1)
1128
064
032
116
18
14
02
01
n 156 = 10011100
count 0
Goal: count the 1s in 156 = 10011100. The naive way tests all 32 columns one at a time. Instead we will delete one set bit per loop, so the loop runs only as many times as there are 1s. Here that should be four.

M2n & -n — isolate the lowest set bit

The mirror image of M1: instead of deleting the lowest 1, it throws away everything else. In two's complement -n is ~n + 1, which happens to match n from the lowest set bit downward and disagree everywhere above it. 12 & -12 == 4. This is the standard way to pick a "splitting bit" in LC 260, and it is how a Fenwick tree walks its indices.

M3Single-bit masks with 1 << i

1 << i is a number with a single 1 in column i. Combine it with the operators above and you get the four things you ever want to do to one bit:

single_bit_ops.pypython
bit = 1 << i              # a mask with only column i turned on

is_set = (n >> i) & 1      # TEST   -> 0 or 1 (shift down, look at the last bit)
n = n | bit               # SET    -> force column i on
n = n & ~bit              # CLEAR  -> force column i off
n = n ^ bit               # TOGGLE -> flip whatever is there

M4Masking — keeping only the part you care about

A mask is just a number whose 1s mark the columns you want to keep. n & 0xFF keeps the low 8 bits and throws the rest away; n & 1 is the "is it odd?" test; (1 << k) - 1 builds a block of k ones, which is the cheapest way to make a custom-width mask.

M5Shifts as cheap arithmetic

n << 1 doubles, n >> 1 halves (rounding down, always — -3 >> 1 == -2, not -1). Walking a number bit by bit with n >>= 1 is the standard fixed-width loop for problems like Reverse Bits.

03 negatives & python

Two's complement, and the gotcha that breaks LC 371

Hardware stores a negative number as its two's complement: flip every bit, then add 1. In 8 bits, 5 is 00000101, so -5 is 11111011. The top bit doubles as the sign, and the beauty of the scheme is that ordinary binary addition then "just works" for negatives with no special cases.

5 = 0 0 0 0 0 1 0 1 ~5 = 1 1 1 1 1 0 1 0 flip every bit +1 = 1 1 1 1 1 0 1 1 = -5 in 8-bit two's complement

Python agrees with all of that in spirit — ~x == -x - 1, and (-5 >> i) & 1 gives you the right two's-complement bit — but with one enormous difference:

The one thing to remember
Python integers are arbitrary precision. They never overflow. There is no 32-bit register to fall off the end of. Conceptually a negative Python int has an infinite run of leading 1s, and a positive one an infinite run of leading 0s — so any algorithm that relies on bits dropping off the top must build its own ceiling with an explicit mask.

That is the whole difficulty of LC 371 (Sum of Two Integers). The carry-propagation loop below is correct C; in Python, a negative operand keeps generating a carry that marches up forever and the while loop never ends. The fix is to & 0xFFFFFFFF after every step, which simulates a 32-bit register, and then translate the result back into a signed Python int at the very end.

1 / 8walkthrough — adding without + (LC 371)
08s
04s
12s
01s
a 2 = 0010
b 3 = 0011
Goal: compute 2 + 3 using only bitwise operators. The cells always show a; b rides along in the chip. Grade-school addition has two halves — the digit sum and the carry — and each half is one operator.
sum_of_two_integers.py — LC 371python
MASK = 0xFFFFFFFF      # 32 ones: "keep only the low 32 columns"
INT_MAX = 0x7FFFFFFF   # largest positive value a signed 32-bit int can hold

def get_sum(a, b):
    # Step into 32-bit land: from here on every value is an unsigned
    # 32-bit bit pattern, not a Python integer.
    a = a & MASK
    b = b & MASK

    while b != 0:
        # Columns where both have a 1 are the ones that carry; the shift
        # moves each carry into the column it is worth.
        carry = ((a & b) << 1) & MASK
        # XOR adds the columns that do NOT carry.
        a = (a ^ b) & MASK
        b = carry

    if a <= INT_MAX:
        return a

    # The top bit is set, so this pattern means a negative number.
    # Undo the two's complement to get Python's own negative int back.
    return ~(a ^ MASK)

The other place this bites: printing. bin(-5) gives '-0b101' — a minus sign glued to the magnitude, not the two's-complement pattern you were expecting. When you want to see the real 32-bit bits, print bin(x & 0xFFFFFFFF) instead.

04 recognition

When to reach for bits

  • The statement says "without using extra memory" or "in O(1) space" — usually a XOR-cancellation problem.
  • The words binary, bit, power of two, complement, mask, parity appear literally.
  • Elements appear a fixed number of times and one is the odd one out (LC 136 / 137 / 260).
  • You need every subset of a set with n ≤ 20 — mask 0 .. 2n-1 enumerates them, and each mask doubles as a DP state (bitmask DP).
  • You are tracking a small set of flags (26 lowercase letters, visited cities) and a hash set feels heavy — pack them into one integer.
  • Two things need comparing column by column: Hamming distance, Gray code, common prefixes.
When NOT to use it
If a plain hash map or sort solves it in the same complexity, write that instead — bit tricks are hard to read and easy to get subtly wrong, and interviewers care more about the clear solution than the clever one. Say the trick exists, then implement whichever you can defend. Also note XOR cancellation needs duplicates in even counts; if elements repeat three times, x ^ x ^ x == x and the trick quietly fails (that is why LC 137 is a different algorithm). And bitmask enumeration is 2n — fine at n = 20, hopeless at n = 40.
05 templates

The four templates to memorize

T1XOR fold — cancel the pairs

single_number.py — LC 136python
def single_number(nums):
    running_xor = 0                 # x ^ 0 == x, so 0 is the safe start
    for num in nums:
        # Every value that appears twice cancels itself out (x ^ x == 0),
        # so after the whole pass only the loner is left standing.
        running_xor = running_xor ^ num
    return running_xor

The same fold solves LC 268 (Missing Number) — XOR all the indices and all the values together, and everything pairs off except the number that was never there:

missing_number.py — LC 268python
def missing_number(nums):
    # Indices run 0..n-1 but the values are drawn from 0..n, so seed the
    # accumulator with n — the one index that has no slot of its own.
    missing = len(nums)
    for index, num in enumerate(nums):
        missing = missing ^ index ^ num
    return missing

T2Brian Kernighan — one loop per set bit

count_set_bits.py — LC 191python
def count_set_bits(n):
    count = 0
    while n != 0:
        # Erase the lowest 1. The loop therefore runs exactly
        # once per set bit, not once per column.
        n = n & (n - 1)
        count += 1
    return count


def is_power_of_two(n):
    # A power of two has exactly one set bit, so a single erase empties it.
    # The n > 0 guard matters: 0 & -1 == 0 would sneak through otherwise.
    return n > 0 and (n & (n - 1)) == 0

In a real interview bin(n).count("1") or n.bit_count() (Python 3.10+) is the honest one-liner — write the loop only when asked to show the mechanism.

T3Fixed-width bit walk

reverse_bits.py — LC 190python
def reverse_bits(n):
    result = 0
    # Exactly 32 iterations — the width is part of the problem statement,
    # and Python will not stop the loop for us the way a register would.
    for _ in range(32):
        lowest_bit = n & 1          # read the bit about to fall off n
        result = (result << 1) | lowest_bit   # push it onto result
        n = n >> 1
    return result

The shape — pull the low bit off one number, push it onto another — is the same one used for binary conversion, parity and Gray code.

T4Bitmask enumeration — every subset

subsets.py — LC 78python
def subsets(nums):
    n = len(nums)
    all_subsets = []

    # There are 2**n subsets and 2**n masks of n bits. Line them up:
    # mask 0b101 with nums = [1, 2, 3] means "take nums[0] and nums[2]".
    for mask in range(1 << n):
        current = []
        for index in range(n):
            if mask & (1 << index):     # is column "index" on in this mask?
                current.append(nums[index])
        all_subsets.append(current)

    return all_subsets

Same idea powers bitmask DP: the mask becomes the state ("which cities have I visited"), and dp[mask] is indexed by an integer instead of a frozenset.

06 worked variants

Three problems that bend the basic tricks

LC 338Counting Bits — DP on the shift

Counting each number from scratch is O(n log n). Instead notice that value and value >> 1 have the same bits except for the one that got shifted off — and we already computed the answer for the smaller number.

counting_bits.py — LC 338python
def count_bits(n):
    bits = [0] * (n + 1)
    for value in range(1, n + 1):
        half = value >> 1           # value with its last bit chopped off
        last_bit = value & 1        # the bit we chopped: 0 or 1
        bits[value] = bits[half] + last_bit
    return bits

LC 137Single Number II — count columns mod 3

Every value appears three times except one. XOR is useless here, so drop to the column level: if a bit is set in a tripled value it contributes 3 to that column's tally, so any column whose tally is not a multiple of 3 belongs to the loner. There is a famous two-variable "ones / twos" version — this one is the one you can explain.

single_number_ii.py — LC 137python
def single_number_ii(nums):
    result = 0

    for bit_position in range(32):
        ones_in_column = 0
        for num in nums:
            # For negatives Python still reports the correct
            # two's-complement bit here, which is what we want.
            if (num >> bit_position) & 1:
                ones_in_column += 1

        # The tripled values contribute 0 or 3; the leftover is the loner's bit.
        if ones_in_column % 3 != 0:
            result = result | (1 << bit_position)

    # Bit 31 set means the 32-bit pattern is negative — Python would
    # otherwise hand back a large positive number.
    if result >= (1 << 31):
        result -= (1 << 32)
    return result

LC 260Single Number III — split into two buckets

Two loners, everything else paired. XORing everything gives first ^ second — not an answer, but every set bit in it is a column where the two loners differ. Pick any one of those columns and it partitions the array into two halves, each containing exactly one loner. Then T1 twice.

single_number_iii.py — LC 260python
def single_number_iii(nums):
    xor_of_both = 0
    for num in nums:
        xor_of_both = xor_of_both ^ num      # pairs vanish; first ^ second remains

    # Isolate one column where the two loners disagree (M2).
    splitting_bit = xor_of_both & -xor_of_both

    first = 0
    second = 0
    for num in nums:
        # Duplicates always land in the same bucket as each other,
        # so they still cancel inside their own bucket.
        if num & splitting_bit:
            first = first ^ num
        else:
            second = second ^ num

    return [first, second]
07 common mistakes

Where bit code goes wrong

Mistake 1 — forgetting Python ints never overflow
Carry-propagation and complement algorithms assume a fixed-width register. In Python there isn't one, so LC 371 hangs forever on a negative input. Any time bits are supposed to fall off the top, you must supply the ceiling yourself with & 0xFFFFFFFF, and convert back to a signed value at the end.
Mistake 2 — operator precedence
&, | and ^ bind looser than ==, + and - in Python — the opposite of what most people assume. Parenthesise every bitwise expression.
precedence.pypython
# WRONG: parses as  n & (1 + 1)  ->  n & 2
if n & 1 + 1:
    ...

# WRONG: parses as  x & (mask == 0). For any non-zero mask that is
# x & False, i.e. 0 -- so the branch never runs.
if x & mask == 0:
    ...

# RIGHT: say exactly what you mean
if (x & mask) == 0:
    ...
Mistake 3 — while n: with a negative n
Right-shifting a negative Python int is an arithmetic shift: it keeps feeding in 1s, so -8 >> 1 is -4, then -2, then -1, then -1 forever. while n: n >>= 1 is an infinite loop. Mask first (n &= 0xFFFFFFFF) or use a fixed for _ in range(32) loop.
Mistake 4 — the power-of-two test without the zero guard
n & (n - 1) == 0 is true for n = 0 as well (and for negatives, and it has the precedence bug above). The correct test is n > 0 and (n & (n - 1)) == 0.
Mistake 5 — assuming XOR cancellation always applies
It only works when the duplicates come in even counts. x ^ x ^ x == x, so with triples the loner is not what survives — LC 137 needs the mod-3 column tally instead. Check the multiplicity in the statement before reaching for the fold.
Mistake 6 — trusting bin() on negatives
bin(-5) returns '-0b101', not a two's-complement pattern, and len(bin(x)) - 2 silently gives the wrong width for negatives. Debug with format(x & 0xFFFFFFFF, "032b") so you are looking at the bits the algorithm actually manipulates.
10 practice

Practice ladder

In this order — easiest first. Tick each one as you solve it, and re-solve anything that took more than 25 minutes two days later.

14 problems
easyLC 191
Brian Kernighan: loop n &= n - 1 once per set bit. Template T2.
easyLC 231
Exactly one set bit: n > 0 and n & (n - 1) == 0. The n > 0 guard is the whole trick.
easyLC 461
XOR the two numbers to mark the differing columns, then count set bits.
easyLC 136
XOR the whole array; pairs cancel via x ^ x = 0. Template T1.
easyLC 268
XOR every index together with every value — everything pairs off except the gap.
easyLC 190
Fixed 32-iteration loop: pull the low bit off n, push it onto result. Template T3.
easyLC 476
Build an all-ones mask as wide as n, then XOR. Shows why the mask width matters.
easyLC 338
DP on the shift: bits[i] = bits[i >> 1] + (i & 1). O(n) instead of O(n log n).
mediumLC 78
Enumerate masks 0 .. 2^n - 1; bit i of the mask means "keep nums[i]". Template T4.
mediumLC 137
Pairs no longer cancel — tally each column across all numbers and take it mod 3.
mediumLC 260
XOR everything, isolate one differing bit with x & -x, then split into two buckets.
mediumLC 201
The answer is the common binary prefix: shift both ends right until they meet.
mediumLC 89
i ^ (i >> 1) generates a sequence where neighbours differ in exactly one bit.
mediumLC 371
XOR = sum without carries, (a & b) << 1 = the carries — plus the 32-bit mask Python needs.