Chapter 6: Bit Manipulation and Binary Representation
6.06Beginner6 topics22 min3 interactive animations

Bit masks for subsets

A bitmask packs a subset of n items into a single integer: bit i is 1 when item i is chosen. Set operations become single instructions, and all 2^n subsets can be enumerated with a simple loop, which is the basis of bitmask dynamic programming.

Learning goals

  • Represent a subset as an int or long and perform set operations with bit operators
  • Enumerate all subsets and all submasks of a mask
  • Iterate over only the set bits of a mask
  • Describe a state-transition over masks and the idea of bitmask DP

Prerequisites

Bitwise operatorsCommon bit tricksBasic recursion

If a pizza has 5 optional toppings, one 5-bit number describes the whole order: 10110 means toppings 1, 2 and 4. Checking whether two orders share a topping is a single AND.

6.06.1

Represent subset

For a universe of n ≤ 31 items (63 with long), the subset with items i1, i2, … is the number (1 << i1) | (1 << i2) | …. Every subset gets a unique number between 0 and 2^n - 1, and the empty set is 0.

Set operations map directly onto bit operators.

Set operationBitmask expression
Contains i(mask >> i & 1) == 1
Add imask | (1 << i)
Remove imask & ~(1 << i)
Uniona | b
Intersectiona & b
Difference a \ ba & ~b
Complement (n items)((1 << n) - 1) ^ mask
Is a ⊆ b(a & b) == a
SizeInteger.bitCount(mask)
6.06.2

Enumerate subsets

The numbers 0 to 2^n - 1 are exactly all subsets of n items. Loop mask over that range and, for each, add item i when bit i is set. The animation lists every subset in order and highlights the chosen items.

The total cost is O(2^n · n). That is fine up to about n = 20-25 (a million to 33 million subsets), and impossible beyond that: the count doubles with every additional item.

Java
1static List<List<Integer>> allSubsets(int[] items) {
2 int n = items.length;
3 List<List<Integer>> result = new ArrayList<>();
4 for (int mask = 0; mask < (1 << n); mask++) {
5 List<Integer> subset = new ArrayList<>();
6 for (int i = 0; i < n; i++) {
7 if ((mask & (1 << i)) != 0) subset.add(items[i]);
8 }
9 result.add(subset);
10 }
11 return result;
12}

Worst

O(n · 2^n)

Space

O(n · 2^n) to store them all

Enumerate all subsets with a bitmask

Interactive · edit the input

Your input

Try

int[] arr · length 3

5
7
9
012
In the subset (bit is 1)

subsets so far

empty
Setup

3 items give 2^3 = 8 subsets. Every number from 0 to 7 is one choice: bit i says whether item i is in.

Step 1 / 10
Java
1int n = items.length;
2for (int mask = 0; mask < (1 << n); mask++) {
3 List<Integer> subset = new ArrayList<>();
4 for (int i = 0; i < n; i++) {
5 if ((mask & (1 << i)) != 0) { // is bit i set?
6 subset.add(items[i]);
7 }
8 }
9 print(subset);
10}

Live state

0 subsets

n

3

2^n

8

No condition evaluated on this step
Execution trace (dry run table)Show 10 steps
StepLineStateWhat happens
1L2n=3 2^n=83 items give 2^3 = 8 subsets. Every number from 0 to 7 is one choice: bit i says whether item i is in.
2L5mask=0 subset={}mask 0 has no bits set: the empty subset.
3L5mask=1 subset={5}mask 1 = 001 in binary. Bits 0 are set, so the subset is {5}.
4L5mask=2 subset={7}mask 2 = 010 in binary. Bits 1 are set, so the subset is {7}.
5L5mask=3 subset={5, 7}mask 3 = 011 in binary. Bits 0, 1 are set, so the subset is {5, 7}.
6L5mask=4 subset={9}mask 4 = 100 in binary. Bits 2 are set, so the subset is {9}.
7L5mask=5 subset={5, 9}mask 5 = 101 in binary. Bits 0, 2 are set, so the subset is {5, 9}.
8L5mask=6 subset={7, 9}mask 6 = 110 in binary. Bits 1, 2 are set, so the subset is {7, 9}.
9L5mask=7 subset={5, 7, 9}mask 7 = 111 in binary. Bits 0, 1, 2 are set, so the subset is {5, 7, 9}.
10L10subsets=88 subsets generated in O(2^n · n). This only works for small n (up to about 20-25), because the count doubles with every item.
6.06.3

Enumerate submasks

To enumerate every subset of a given mask m, start at sub = m and repeatedly step to the next smaller submask with sub = (sub - 1) & m. Subtracting 1 may switch on bits outside m; ANDing with m removes them and lands on the next submask in decreasing order. Stop when sub reaches 0 and handle the empty submask separately.

A mask with k set bits has 2^k submasks, and enumerating all submasks of all masks of n bits costs 3^n in total (each item is in the mask and in the submask, in the mask but not the submask, or in neither).

Java
1for (int sub = mask; sub > 0; sub = (sub - 1) & mask) {
2 process(sub);
3}
4process(0); // the empty submask

Enumerate the submasks of a mask

Interactive · edit the input

Your input

Try

mask = 22

05
14
03
12
11
00

submasks so far

empty
Setup

22 has 3 set bits, so it has 2^3 = 8 submasks. Start with sub = mask.

Step 1 / 9
Java
1for (int sub = mask; sub > 0; sub = (sub - 1) & mask) {
2 process(sub); // next submask, in decreasing order
3}
4process(0); // the empty submask

Live state

0 submasks

mask

22

No condition evaluated on this step
Execution trace (dry run table)Show 9 steps
StepLineStateWhat happens
1L1mask=2222 has 3 set bits, so it has 2^3 = 8 submasks. Start with sub = mask.
2L2sub=22 sub - 1=21 next=20Process 010110. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 010100.
3L2sub=20 sub - 1=19 next=18Process 010100. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 010010.
4L2sub=18 sub - 1=17 next=16Process 010010. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 010000.
5L2sub=16 sub - 1=15 next=6Process 010000. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 000110.
6L2sub=6 sub - 1=5 next=4Process 000110. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 000100.
7L2sub=4 sub - 1=3 next=2Process 000100. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 000010.
8L2sub=2 sub - 1=1 next=0Process 000010. Next: sub - 1 steps down by one and may land on bits outside the mask; ANDing with the mask removes them and jumps to the next smaller submask: 000000.
9L4submasks=8The loop stops at sub = 0, so the empty submask is handled separately. 8 submasks in total.
6.06.4

Iterate set bits

To visit only the 1 bits of a mask, repeatedly take the lowest set bit and remove it: for (m = mask; m != 0; m &= m - 1), with Integer.numberOfTrailingZeros(m) giving its index. This runs once per set bit, so sparse masks are cheap.

This is the standard way to loop over the members of a subset, or over the neighbours of a vertex stored as a bitmask in a graph algorithm.

Worst

O(number of set bits)

Space

O(1)

Iterate over the set bits of a mask

Interactive · edit the input

Your input

Try

mask = 148

17
06
05
14
03
12
01
00

indices visited

empty
Setup

Visit only the positions of the 1 bits, skipping every zero.

Step 1 / 5
Java
1for (int m = mask; m != 0; m &= m - 1) {
2 int lowest = m & -m; // isolate the lowest set bit
3 int index = Integer.numberOfTrailingZeros(m); // its position
4 visit(index);
5}

Live state

0 visits

mask

148

No condition evaluated on this step
Execution trace (dry run table)Show 5 steps
StepLineStateWhat happens
1L1mask=148Visit only the positions of the 1 bits, skipping every zero.
2L3m=148 lowest=4 index=2m & -m isolates the lowest set bit 4, which is bit 2 (numberOfTrailingZeros). Then m &= m - 1 removes it.
3L3m=144 lowest=16 index=4m & -m isolates the lowest set bit 16, which is bit 4 (numberOfTrailingZeros). Then m &= m - 1 removes it.
4L3m=128 lowest=128 index=7m & -m isolates the lowest set bit 128, which is bit 7 (numberOfTrailingZeros). Then m &= m - 1 removes it.
5L5visits=33 iterations, one per set bit.
6.06.5

State transitions

A mask can describe the state of a process: which items are already used, which switches are on, which cells are filled. A move that adds item i is mask | (1 << i), a move that toggles switch i is mask ^ (1 << i), and the goal state is a specific mask such as (1 << n) - 1.

Because there are at most 2^n states, a breadth-first search over masks, or a table indexed by mask, solves puzzles like lights-out and the shortest way to visit all items.

Java
1// fewest moves to turn all n lights on, when each button toggles a set of lights
2static int fewestPresses(int n, int[] buttonMask) {
3 int full = (1 << n) - 1;
4 int[] dist = new int[1 << n];
5 Arrays.fill(dist, -1);
6 dist[0] = 0;
7 Deque<Integer> queue = new ArrayDeque<>(List.of(0));
8 while (!queue.isEmpty()) {
9 int state = queue.poll();
10 if (state == full) return dist[state];
11 for (int b : buttonMask) {
12 int next = state ^ b; // press the button
13 if (dist[next] < 0) { dist[next] = dist[state] + 1; queue.add(next); }
14 }
15 }
16 return -1;
17}
6.06.6

Bitmask DP introduction

Bitmask DP stores one value per subset: dp[mask] is the best answer for exactly the items in mask. You compute it from smaller masks by adding one item at a time. The table has 2^n entries and each transition tries n items, giving O(n · 2^n), far better than the n! of trying every order.

Classic examples are assigning n tasks to n people at minimum cost and the travelling salesman problem (O(n² · 2^n)). Dynamic programming itself is covered in Chapter 23; here is the shape.

Java
1// minimum total cost to give each person i exactly one task, n <= 20
2static int assign(int[][] cost) {
3 int n = cost.length;
4 int[] dp = new int[1 << n];
5 Arrays.fill(dp, Integer.MAX_VALUE);
6 dp[0] = 0;
7 for (int mask = 0; mask < (1 << n); mask++) {
8 if (dp[mask] == Integer.MAX_VALUE) continue;
9 int person = Integer.bitCount(mask); // tasks used so far = next person
10 if (person == n) continue;
11 for (int task = 0; task < n; task++) {
12 if ((mask & (1 << task)) != 0) continue;
13 int next = mask | (1 << task);
14 dp[next] = Math.min(dp[next], dp[mask] + cost[person][task]);
15 }
16 }
17 return dp[(1 << n) - 1];
18}

Worst

O(n · 2^n)

Space

O(2^n)

Practice ladder

Now try it yourself

Easy

  • Generate all subsets of {1, 2, 3} with a bitmask
  • Check whether set A is a subset of set B using masks

Medium

  • Subsets of an array that sum to k
  • Maximum product of word lengths with no common letters (26-bit masks)
  • Count the subsets of a mask of size k

Advanced

  • Assign tasks to people with bitmask DP
  • Travelling salesman for n ≤ 15
  • Count the pairs (sub, mask) with sub ⊆ mask over n bits and verify it is 3^n

Key takeaways

  • A subset of up to 31 (or 63) items is one integer; set operations are single bit operations.
  • Loop mask from 0 to 2^n - 1 for all subsets; use sub = (sub - 1) & mask for submasks.
  • Bitmask DP keeps one value per subset: O(n · 2^n) instead of n!.