Skip to content

Expert: advanced patterns and interviews · Tries, union-find and bit manipulation · topic 51 of 61 · 35 min

Bit tricks in interviews

Explanation

A handful of bit tricks come up again and again. XOR cancels pairs, so it finds the single number in a list of pairs and the missing number in 0..n with O(1) extra space. Counting from 0 to 2ⁿ − 1 and reading the bits of each number lists every subset of n items. The Hamming distance of two numbers is the popcount of their XOR.

Three rules of XOR

  • x ^ 0 = x: XOR with 0 changes nothing.
  • x ^ x = 0: a number XOR itself cancels out.
  • Order does not matter: a ^ b ^ c is the same in any order and any grouping.

Together they mean: if you XOR a list in which every value appears twice except one, all the pairs cancel and only the lonely value remains.

Single number

Problem: every number in a list appears exactly twice, except one. Find it. A dictionary of counts works in O(n) time but uses O(n) extra space. XOR-ing everything gives the answer in O(n) time and O(1) extra space.

python
from functools import reduce
from operator import xor

nums = [4, 1, 2, 1, 2]
print(reduce(xor, nums, 0))   # Output: 4
4 ^ 1 ^ 2 ^ 1 ^ 2 = 4 ^ (1 ^ 1) ^ (2 ^ 2) = 4 ^ 0 ^ 0 = 4.

Missing number

Problem: a list holds n different numbers from 0 to n, so exactly one is missing. The sum way: the numbers 0..n add up to n(n + 1)/2, so the missing one is that total minus sum(nums). The XOR way: XOR all of 0..n together with all of nums; every present number appears twice and cancels, leaving the missing one. In Python the sum cannot overflow, but in languages with 32-bit ints a big sum can, while XOR never does.

All subsets with bitmasks

A set of n items has 2ⁿ subsets. Number them with masks from 0 to 2ⁿ − 1, that is range(1 << n). In mask, bit i says whether item i is in the subset. Counting through the masks lists every subset exactly once, with no recursion. The cost is O(n × 2ⁿ), the same as backtracking (Module 5), because the output itself has that size. It is practical only for small n, about 20 or less.

python
items = ["a", "b", "c"]
n = len(items)
for mask in range(1 << n):
    subset = [items[i] for i in range(n) if mask & (1 << i)]
    print(f"{mask:03b}", subset)
# Output:
# 000 []
# 001 ['a']
# 010 ['b']
# 011 ['a', 'b']
# 100 ['c']
# 101 ['a', 'c']
# 110 ['b', 'c']
# 111 ['a', 'b', 'c']
Bit 0 (the rightmost) is item "a", bit 1 is "b", bit 2 is "c".

Hamming distance

The Hamming distance between two numbers is the number of bit positions where they differ. XOR puts a 1 exactly where the bits differ, so the distance is the popcount of x ^ y. It is used in error-detecting codes and for comparing short "fingerprints" of files or images.

Gray code in one line

In a Gray code sequence, neighbouring numbers differ in exactly one bit. The i-th Gray code is i ^ (i >> 1): for i = 0, 1, 2, 3 it gives 0, 1, 3, 2, which is 00, 01, 11, 10. Rotary position sensors use Gray codes so that a reading taken between two positions is never wildly wrong.

Swapping without a temporary variable

python
a, b = 5, 9
a ^= b
b ^= a
a ^= b
print(a, b)   # Output: 9 5
A classic puzzle. In Python, just write a, b = b, a.

This XOR swap is a nice puzzle but a bad habit in real code. It is harder to read, it is usually no faster on modern hardware, and if both names refer to the same memory location (for example swapping arr[i] with arr[j] when i == j), it sets the value to 0.

Preview: bitmask dynamic programming

A mask can also stand for "which items have I used so far". For example, the cheapest tour through n cities can be solved with a table indexed by (mask of visited cities, current city) in O(2ⁿ × n²) time, which is practical for n up to about 20. Module 11 covers dynamic programming.

Common mistakes

  • Using the XOR trick when the other numbers appear three times. Pairs cancel; triples do not. Fix: count bits per position modulo 3, or use a dictionary.
  • Writing mask & i instead of mask & (1 << i) when testing bit i. Fix: always build the mask with a shift.
  • Writing 2 ^ n for 2ⁿ. Fix: 1 << n or 2 ** n.
  • Hamming distance with negative numbers in Python: x ^ y can be negative, and bin() then hides the bit pattern. Fix: keep inputs non-negative, or mask both with & 0xFFFFFFFF.
  • Using XOR swap on two names that may refer to the same element. Fix: use a normal swap.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Bit tricks in interviews · 1/8

Bit tricks

This lesson collects the bit tricks that interviewers love, starting with XOR.

Prefer a video by a teacher? Find free videos on “bit manipulation interview problems single number missing number subsets bitmask” on YouTube ↗

In real life: Spotting near-duplicate photos

Photo apps and content platforms often need to spot images that are almost the same, such as the same photo resized or slightly brightened. A common approach turns each image into a short "perceptual hash", a number whose bits describe the rough light and dark pattern of the picture. Similar images get hashes that differ in only a few bits, so the Hamming distance (popcount of XOR) is a fast similarity test. Real systems use 64-bit or longer hashes and tuned thresholds; the 16-bit values below are made up.

python
h1 = 0b1011_0110_1100_0011
h2 = 0b1011_0100_1100_0111
h3 = 0b0100_1001_0011_1100
print(bin(h1 ^ h2).count("1"))   # similar photos
print(bin(h1 ^ h3).count("1"))   # different photos
Shows 2, then 16: h1 and h2 differ in two bits; h3 is the exact opposite of h1.

Practice (0/4 done)

Task 1 · Code · Python

Write single_number(nums). Every value in nums appears exactly twice, except one value that appears once. Return that value. Use XOR: O(n) time and O(1) extra space (no dictionary or set). Values may be negative. Example: single_number([4, 1, 2, 1, 2]) returns 4.

Task 2 · Code · Python

Write missing_number(nums). nums holds n DIFFERENT numbers taken from 0, 1, …, n, so exactly one number in that range is missing. Return it. O(n) time, O(1) extra space: use XOR (or the sum formula). An empty list means n = 0, so the missing number is 0. Example: missing_number([3, 0, 1]) returns 2.

Task 3 · Code · Python

Write subsets_bitmask(nums) that returns ALL subsets of nums as lists, using bitmasks. Order the subsets by mask from 0 to 2ⁿ − 1, where bit i of the mask means nums[i] is included, and keep the items of each subset in their original order. Treat items at different positions as different even if equal. Example: subsets_bitmask([1, 2, 3]) returns [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]].

Task 4 · Code · Python

Write hamming_distance(x, y) for x, y ≥ 0. Return the number of bit positions where x and y differ. Hint: XOR them, then count the 1 bits. Example: hamming_distance(1, 4) returns 2, because 001 and 100 differ in two places.

Quick quiz

Question 1

1.What is 7 ^ 3 ^ 7?

Question 2

2.Why might XOR be preferred over the sum formula for "missing number" in Java or C++?

Question 3

3.How many masks do you loop over to list every subset of 4 items?

Question 4

4.Items are [a, b, c] and bit i stands for item i. Which subset does mask 5 (binary 101) mean?

Question 5

5.What is the Gray code i ^ (i >> 1) for i = 3?

Question 6

6.Why avoid the XOR swap in real code?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is 7 ^ 3 ^ 7?

Answer: 3

The two 7s cancel, leaving 3.

Why might XOR be preferred over the sum formula for "missing number" in Java or C++?

Answer: A big sum can overflow a fixed-size int; XOR cannot

n(n + 1)/2 can exceed 32 bits; XOR results never grow beyond the inputs' width.

How many masks do you loop over to list every subset of 4 items?

Answer: 16

2⁴ = 16 masks: 0 to 15.

Items are [a, b, c] and bit i stands for item i. Which subset does mask 5 (binary 101) mean?

Answer: [a, c]

Bits 0 and 2 are set, so items 0 and 2: a and c.

What is the Gray code i ^ (i >> 1) for i = 3?

Answer: 2

3 is 11, 3 >> 1 is 01, and 11 XOR 01 = 10 = 2.

Why avoid the XOR swap in real code?

Answer: It is harder to read, and it sets the value to 0 if both names are the same element

a ^= a gives 0, so swapping an element with itself destroys it.

Why does "while n: n >>= 1" never end for n = −1 in Python?Related

Answer: -1 >> 1 is still -1

Right-shifting a negative Python int rounds down, so −1 stays −1 forever.

From Bits: AND, OR, XOR and shifts

How many subsets does a set of 6 distinct items have?Related

Answer: 64

Each item is in or out: 2⁶ = 64, including the empty set.

From Backtracking: subsets and permutations

Target sum: numbers [1, 1, 1, 1, 1], target 3, each with + or −. Which subset sum P do you count?Related

Answer: P = 4

P = (total + target) / 2 = (5 + 3) / 2 = 4. There are 5 ways to choose four 1s.

From Knapsack problems

Why use unsigned types for bit manipulation?Related

Answer: Shifts and overflow are well defined for unsigned types

Signed shifts have implementation-defined or undefined cases.

From Bitwise operators

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Tries, union-find and bit manipulation”. The module exam comes next.
  2. Module 10 exam: Tries, union-find and bits · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Dynamic programming. 61 topics left in the course before the final exam.

← Previous: Bits: AND, OR, XOR and shifts