Subset and permutation generation
Generating subsets, the power set, subsequences, all permutations, unique permutations of multisets and combinations, with the exact loop shapes and counts for each.
Learning goals
- Write the start-index loop for subsets and combinations
- Write the used-array loop for permutations
- Skip duplicates correctly by sorting first
- State the number of results for each family
Prerequisites
Subsets
To list all subsets of an array, record the current path at *every* node, and let each node extend the path with elements that come later in the array. Passing i + 1 as the next start index guarantees each subset is built in increasing index order, so no subset appears twice. n elements give 2^n subsets.
In the animation every node is a subset and every edge adds one value. Try the “Four values (16 subsets)” preset to see the tree double with each extra element.
1static List<List<Integer>> subsets(int[] a) {2List<List<Integer>> out = new ArrayList<>();3collect(a, 0, new ArrayList<>(), out);4return out;5}6private static void collect(int[] a, int start, List<Integer> path, List<List<Integer>> out) {7out.add(new ArrayList<>(path)); // every partial solution is a subset8for (int i = start; i < a.length; i++) {9path.add(a[i]);10collect(a, i + 1, path, out);11path.remove(path.size() - 1);12}13}
Best
O(n · 2^n)
Average
O(n · 2^n)
Worst
O(n · 2^n)
Space
O(n) recursion, plus the output
Subsets (power set) by backtracking
Interactive · edit the inputpartial solution
solutions found (0)
Each value is either in the subset or not. Backtracking builds the subsets one value at a time: choose a value, explore everything after it, then unchoose. 3 values give 2^3 = 8 subsets.
1static void subsets(int[] a, int start, List<Integer> path, List<List<Integer>> out) {2out.add(new ArrayList<>(path)); // every partial solution is a subset3for (int i = start; i < a.length; i++) {4path.add(a[i]); // choose5subsets(a, i + 1, path, out); // explore6path.remove(path.size() - 1); // unchoose7}8}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | Each value is either in the subset or not. Backtracking builds the subsets one value at a time: choose a value, explore everything after it, then unchoose. 3 values give 2^3 = 8 subsets. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L2 | depth=1 solutions=1 | Record []: every partial solution counts here. |
| 4 | L4 | depth=1 solutions=1 | Choose 1. |
| 5 | L1 | depth=2 solutions=1 | Explore [1]. |
| 6 | L2 | depth=2 solutions=2 | Record [1]: every partial solution counts here. |
| 7 | L4 | depth=2 solutions=2 | Choose 2. |
| 8 | L1 | depth=3 solutions=2 | Explore [1, 2]. |
| 9 | L2 | depth=3 solutions=3 | Record [1, 2]: every partial solution counts here. |
| 10 | L4 | depth=3 solutions=3 | Choose 3. |
| 11 | L1 | depth=4 solutions=3 | Explore [1, 2, 3]. |
| 12 | L2 | depth=4 solutions=4 | Record [1, 2, 3]: every partial solution counts here. |
| 13 | L6 | depth=3 solutions=4 | Unchoose 3: back to [1, 2]. |
| 14 | L6 | depth=2 solutions=4 | Unchoose 2: back to [1]. |
| 15 | L4 | depth=2 solutions=4 | Choose 3. |
| 16 | L1 | depth=3 solutions=4 | Explore [1, 3]. |
| 17 | L2 | depth=3 solutions=5 | Record [1, 3]: every partial solution counts here. |
| 18 | L6 | depth=2 solutions=5 | Unchoose 3: back to [1]. |
| 19 | L6 | depth=1 solutions=5 | Unchoose 1: back to []. |
| 20 | L4 | depth=1 solutions=5 | Choose 2. |
| 21 | L1 | depth=2 solutions=5 | Explore [2]. |
| 22 | L2 | depth=2 solutions=6 | Record [2]: every partial solution counts here. |
| 23 | L4 | depth=2 solutions=6 | Choose 3. |
| 24 | L1 | depth=3 solutions=6 | Explore [2, 3]. |
| 25 | L2 | depth=3 solutions=7 | Record [2, 3]: every partial solution counts here. |
| 26 | L6 | depth=2 solutions=7 | Unchoose 3: back to [2]. |
| 27 | L6 | depth=1 solutions=7 | Unchoose 2: back to []. |
| 28 | L4 | depth=1 solutions=7 | Choose 3. |
| 29 | L1 | depth=2 solutions=7 | Explore [3]. |
| 30 | L2 | depth=2 solutions=8 | Record [3]: every partial solution counts here. |
| 31 | L6 | depth=1 solutions=8 | Unchoose 3: back to []. |
| 32 | L8 | solutions=8 nodes=8 return=[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]] | Search finished: 8 solutions, 7 choices tried. Backtracking explores every branch the pruning rules allow. |
Power set
The **power set** is the set of all subsets, 2^n of them. There is a second way to generate it without recursion: every subset of n elements corresponds to an n-bit number, where bit i says whether element i is included. Counting from 0 to 2^n − 1 visits every subset exactly once (this is the technique of Chapter 6, Bit Manipulation).
The bitmask loop is shorter and has no stack, but it works only for n up to about 30 (an int mask) or 62 (a long), and it cannot prune. Backtracking can stop early; the mask loop cannot. The existing animation “Enumerate subsets” in Chapter 6 shows the masks.
1static List<List<Integer>> powerSet(int[] a) {2int n = a.length;3List<List<Integer>> out = new ArrayList<>();4for (int mask = 0; mask < (1 << n); mask++) { // each mask is one subset5List<Integer> subset = new ArrayList<>();6for (int i = 0; i < n; i++) if ((mask >> i & 1) == 1) subset.add(a[i]);7out.add(subset);8}9return out;10}
Enumerate all subsets with a bitmask
Interactive · edit the inputint[] arr · length 3
subsets so far
3 items give 2^3 = 8 subsets. Every number from 0 to 7 is one choice: bit i says whether item i is in.
1int n = items.length;2for (int mask = 0; mask < (1 << n); mask++) {3List<Integer> subset = new ArrayList<>();4for (int i = 0; i < n; i++) {5if ((mask & (1 << i)) != 0) { // is bit i set?6subset.add(items[i]);7}8}9print(subset);10}
Live state
n
3
2^n
8
Execution trace (dry run table)Show 10 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=3 2^n=8 | 3 items give 2^3 = 8 subsets. Every number from 0 to 7 is one choice: bit i says whether item i is in. |
| 2 | L5 | mask=0 subset={} | mask 0 has no bits set: the empty subset. |
| 3 | L5 | mask=1 subset={5} | mask 1 = 001 in binary. Bits 0 are set, so the subset is {5}. |
| 4 | L5 | mask=2 subset={7} | mask 2 = 010 in binary. Bits 1 are set, so the subset is {7}. |
| 5 | L5 | mask=3 subset={5, 7} | mask 3 = 011 in binary. Bits 0, 1 are set, so the subset is {5, 7}. |
| 6 | L5 | mask=4 subset={9} | mask 4 = 100 in binary. Bits 2 are set, so the subset is {9}. |
| 7 | L5 | mask=5 subset={5, 9} | mask 5 = 101 in binary. Bits 0, 2 are set, so the subset is {5, 9}. |
| 8 | L5 | mask=6 subset={7, 9} | mask 6 = 110 in binary. Bits 1, 2 are set, so the subset is {7, 9}. |
| 9 | L5 | mask=7 subset={5, 7, 9} | mask 7 = 111 in binary. Bits 0, 1, 2 are set, so the subset is {5, 7, 9}. |
| 10 | L10 | subsets=8 | 8 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. |
Subsequences
A **subsequence** keeps the original order but may skip elements; a subset ignores order. For an array or string with distinct positions, the subsequences are exactly the subsets of the *positions*, so the same code generates them: choose indices in increasing order. A string of length n has 2^n subsequences (counting the empty one); duplicates appear if the string has repeated letters, such as the two a's in "aab".
To list distinct subsequences of a string with repeats, sort the characters and skip a character equal to the previous one at the same level (the rule used for unique permutations below), or collect results in a Set.
1static List<String> subsequences(String s) {2List<String> out = new ArrayList<>();3pick(s, 0, new StringBuilder(), out);4return out;5}6private static void pick(String s, int start, StringBuilder sb, List<String> out) {7out.add(sb.toString()); // "" is a subsequence too8for (int i = start; i < s.length(); i++) {9sb.append(s.charAt(i)); // choose10pick(s, i + 1, sb, out); // explore11sb.deleteCharAt(sb.length() - 1); // unchoose12}13}
All permutations
A permutation uses every element exactly once, so at each level the loop tries *every* index that is not yet used. A used[] array tracks which ones are on the path. n distinct elements have n! permutations; the tree has n + n(n−1) + … nodes, about e·n!.
The animation lists the 6 permutations of {1, 2, 3} in a tree of 31 nodes: 16 real nodes (1 + 3 + 6 + 6) and 15 rejected ✗ options, which show what the used[i] check prevents. Four values already produce 24 permutations; the factorial growth is why exhaustive search stops being practical past about n = 10 or 11.
1static List<List<Integer>> permutations(int[] a) {2List<List<Integer>> out = new ArrayList<>();3arrange(a, new boolean[a.length], new ArrayList<>(), out);4return out;5}6private static void arrange(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out) {7if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }8for (int i = 0; i < a.length; i++) {9if (used[i]) continue;10used[i] = true; path.add(a[i]);11arrange(a, used, path, out);12path.remove(path.size() - 1); used[i] = false;13}14}
Best
O(n · n!)
Average
O(n · n!)
Worst
O(n · n!)
Space
O(n) recursion, plus the output
Permutations, including unique permutations with duplicates
Interactive · edit the inputpartial solution
solutions found (0)
at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings.
1static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out, boolean unique) { // sort a first when unique2if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }3for (int i = 0; i < a.length; i++) {4if (used[i]) continue; // already on the path5if (unique && i > 0 && a[i] == a[i - 1] && !used[i - 1]) continue; // a twin of the previous value, which is free6used[i] = true; path.add(a[i]); // choose7permute(a, used, path, out, unique); // explore8path.remove(path.size() - 1); used[i] = false; // unchoose9}10}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 69 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L6 | depth=1 solutions=0 | Choose 1. |
| 4 | L1 | depth=2 solutions=0 | Explore [1]. |
| 5 | L4 | depth=3 solutions=0 | Reject 1: already on the path. |
| 6 | L6 | depth=2 solutions=0 | Choose 2. |
| 7 | L1 | depth=3 solutions=0 | Explore [1, 2]. |
| 8 | L4 | depth=4 solutions=0 | Reject 1: already on the path. |
| 9 | L4 | depth=4 solutions=0 | Reject 2: already on the path. |
| 10 | L6 | depth=3 solutions=0 | Choose 3. |
| 11 | L1 | depth=4 solutions=0 | Explore [1, 2, 3]. |
| 12 | L2 | depth=4 solutions=1 | Complete and valid: record [1, 2, 3]. |
| 13 | L8 | depth=3 solutions=1 | Unchoose 3: back to [1, 2]. |
| 14 | L8 | depth=2 solutions=1 | Unchoose 2: back to [1]. |
| 15 | L6 | depth=2 solutions=1 | Choose 3. |
| 16 | L1 | depth=3 solutions=1 | Explore [1, 3]. |
| 17 | L4 | depth=4 solutions=1 | Reject 1: already on the path. |
| 18 | L6 | depth=3 solutions=1 | Choose 2. |
| 19 | L1 | depth=4 solutions=1 | Explore [1, 3, 2]. |
| 20 | L2 | depth=4 solutions=2 | Complete and valid: record [1, 3, 2]. |
| 21 | L8 | depth=3 solutions=2 | Unchoose 2: back to [1, 3]. |
| 22 | L4 | depth=4 solutions=2 | Reject 3: already on the path. |
| 23 | L8 | depth=2 solutions=2 | Unchoose 3: back to [1]. |
| 24 | L8 | depth=1 solutions=2 | Unchoose 1: back to []. |
| 25 | L6 | depth=1 solutions=2 | Choose 2. |
| 26 | L1 | depth=2 solutions=2 | Explore [2]. |
| 27 | L6 | depth=2 solutions=2 | Choose 1. |
| 28 | L1 | depth=3 solutions=2 | Explore [2, 1]. |
| 29 | L4 | depth=4 solutions=2 | Reject 1: already on the path. |
| 30 | L4 | depth=4 solutions=2 | Reject 2: already on the path. |
| 31 | L6 | depth=3 solutions=2 | Choose 3. |
| 32 | L1 | depth=4 solutions=2 | Explore [2, 1, 3]. |
| 33 | L2 | depth=4 solutions=3 | Complete and valid: record [2, 1, 3]. |
| 34 | L8 | depth=3 solutions=3 | Unchoose 3: back to [2, 1]. |
| 35 | L8 | depth=2 solutions=3 | Unchoose 1: back to [2]. |
| 36 | L4 | depth=3 solutions=3 | Reject 2: already on the path. |
| 37 | L6 | depth=2 solutions=3 | Choose 3. |
| 38 | L1 | depth=3 solutions=3 | Explore [2, 3]. |
| 39 | L6 | depth=3 solutions=3 | Choose 1. |
| 40 | L1 | depth=4 solutions=3 | Explore [2, 3, 1]. |
| 41 | L2 | depth=4 solutions=4 | Complete and valid: record [2, 3, 1]. |
| 42 | L8 | depth=3 solutions=4 | Unchoose 1: back to [2, 3]. |
| 43 | L4 | depth=4 solutions=4 | Reject 2: already on the path. |
| 44 | L4 | depth=4 solutions=4 | Reject 3: already on the path. |
| 45 | L8 | depth=2 solutions=4 | Unchoose 3: back to [2]. |
| 46 | L8 | depth=1 solutions=4 | Unchoose 2: back to []. |
| 47 | L6 | depth=1 solutions=4 | Choose 3. |
| 48 | L1 | depth=2 solutions=4 | Explore [3]. |
| 49 | L6 | depth=2 solutions=4 | Choose 1. |
| 50 | L1 | depth=3 solutions=4 | Explore [3, 1]. |
| 51 | L4 | depth=4 solutions=4 | Reject 1: already on the path. |
| 52 | L6 | depth=3 solutions=4 | Choose 2. |
| 53 | L1 | depth=4 solutions=4 | Explore [3, 1, 2]. |
| 54 | L2 | depth=4 solutions=5 | Complete and valid: record [3, 1, 2]. |
| 55 | L8 | depth=3 solutions=5 | Unchoose 2: back to [3, 1]. |
| 56 | L4 | depth=4 solutions=5 | Reject 3: already on the path. |
| 57 | L8 | depth=2 solutions=5 | Unchoose 1: back to [3]. |
| 58 | L6 | depth=2 solutions=5 | Choose 2. |
| 59 | L1 | depth=3 solutions=5 | Explore [3, 2]. |
| 60 | L6 | depth=3 solutions=5 | Choose 1. |
| 61 | L1 | depth=4 solutions=5 | Explore [3, 2, 1]. |
| 62 | L2 | depth=4 solutions=6 | Complete and valid: record [3, 2, 1]. |
| 63 | L8 | depth=3 solutions=6 | Unchoose 1: back to [3, 2]. |
| 64 | L4 | depth=4 solutions=6 | Reject 2: already on the path. |
| 65 | L4 | depth=4 solutions=6 | Reject 3: already on the path. |
| 66 | L8 | depth=2 solutions=6 | Unchoose 2: back to [3]. |
| 67 | L4 | depth=3 solutions=6 | Reject 3: already on the path. |
| 68 | L8 | depth=1 solutions=6 | Unchoose 3: back to []. |
| 69 | L10 | solutions=6 nodes=31 return=[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] | Search finished: 6 solutions, 30 choices tried. Backtracking explores every branch the pruning rules allow. |
Unique permutations with duplicates
If the input has repeated values, plain permutation code lists identical results more than once: for {1, 1, 2} it produces 6 lists but only 3 are different. The fix is to **sort** the array and skip an element that equals the previous one *when the previous one is not currently used*: that means we are about to start a branch that a twin already explored at this level.
Choose “Duplicates: repeated results” and then “Duplicates: unique only” in the animation. The same input needs 31 nodes without the rule and 19 with it, and the answers shrink from 6 to the 3 distinct arrangements [1, 1, 2], [1, 2, 1], [2, 1, 1].
1static List<List<Integer>> uniquePermutations(int[] a) {2int[] sorted = a.clone(); Arrays.sort(sorted);3List<List<Integer>> out = new ArrayList<>();4arrangeUnique(sorted, new boolean[sorted.length], new ArrayList<>(), out);5return out;6}7private static void arrangeUnique(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out) {8if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }9for (int i = 0; i < a.length; i++) {10if (used[i]) continue;11if (i > 0 && a[i] == a[i - 1] && !used[i - 1]) continue; // a twin of the previous value, which is free12used[i] = true; path.add(a[i]);13arrangeUnique(a, used, path, out);14path.remove(path.size() - 1); used[i] = false;15}16}
Permutations, including unique permutations with duplicates
Interactive · edit the inputpartial solution
solutions found (0)
at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings.
1static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out, boolean unique) { // sort a first when unique2if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }3for (int i = 0; i < a.length; i++) {4if (used[i]) continue; // already on the path5if (unique && i > 0 && a[i] == a[i - 1] && !used[i - 1]) continue; // a twin of the previous value, which is free6used[i] = true; path.add(a[i]); // choose7permute(a, used, path, out, unique); // explore8path.remove(path.size() - 1); used[i] = false; // unchoose9}10}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 69 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L6 | depth=1 solutions=0 | Choose 1. |
| 4 | L1 | depth=2 solutions=0 | Explore [1]. |
| 5 | L4 | depth=3 solutions=0 | Reject 1: already on the path. |
| 6 | L6 | depth=2 solutions=0 | Choose 2. |
| 7 | L1 | depth=3 solutions=0 | Explore [1, 2]. |
| 8 | L4 | depth=4 solutions=0 | Reject 1: already on the path. |
| 9 | L4 | depth=4 solutions=0 | Reject 2: already on the path. |
| 10 | L6 | depth=3 solutions=0 | Choose 3. |
| 11 | L1 | depth=4 solutions=0 | Explore [1, 2, 3]. |
| 12 | L2 | depth=4 solutions=1 | Complete and valid: record [1, 2, 3]. |
| 13 | L8 | depth=3 solutions=1 | Unchoose 3: back to [1, 2]. |
| 14 | L8 | depth=2 solutions=1 | Unchoose 2: back to [1]. |
| 15 | L6 | depth=2 solutions=1 | Choose 3. |
| 16 | L1 | depth=3 solutions=1 | Explore [1, 3]. |
| 17 | L4 | depth=4 solutions=1 | Reject 1: already on the path. |
| 18 | L6 | depth=3 solutions=1 | Choose 2. |
| 19 | L1 | depth=4 solutions=1 | Explore [1, 3, 2]. |
| 20 | L2 | depth=4 solutions=2 | Complete and valid: record [1, 3, 2]. |
| 21 | L8 | depth=3 solutions=2 | Unchoose 2: back to [1, 3]. |
| 22 | L4 | depth=4 solutions=2 | Reject 3: already on the path. |
| 23 | L8 | depth=2 solutions=2 | Unchoose 3: back to [1]. |
| 24 | L8 | depth=1 solutions=2 | Unchoose 1: back to []. |
| 25 | L6 | depth=1 solutions=2 | Choose 2. |
| 26 | L1 | depth=2 solutions=2 | Explore [2]. |
| 27 | L6 | depth=2 solutions=2 | Choose 1. |
| 28 | L1 | depth=3 solutions=2 | Explore [2, 1]. |
| 29 | L4 | depth=4 solutions=2 | Reject 1: already on the path. |
| 30 | L4 | depth=4 solutions=2 | Reject 2: already on the path. |
| 31 | L6 | depth=3 solutions=2 | Choose 3. |
| 32 | L1 | depth=4 solutions=2 | Explore [2, 1, 3]. |
| 33 | L2 | depth=4 solutions=3 | Complete and valid: record [2, 1, 3]. |
| 34 | L8 | depth=3 solutions=3 | Unchoose 3: back to [2, 1]. |
| 35 | L8 | depth=2 solutions=3 | Unchoose 1: back to [2]. |
| 36 | L4 | depth=3 solutions=3 | Reject 2: already on the path. |
| 37 | L6 | depth=2 solutions=3 | Choose 3. |
| 38 | L1 | depth=3 solutions=3 | Explore [2, 3]. |
| 39 | L6 | depth=3 solutions=3 | Choose 1. |
| 40 | L1 | depth=4 solutions=3 | Explore [2, 3, 1]. |
| 41 | L2 | depth=4 solutions=4 | Complete and valid: record [2, 3, 1]. |
| 42 | L8 | depth=3 solutions=4 | Unchoose 1: back to [2, 3]. |
| 43 | L4 | depth=4 solutions=4 | Reject 2: already on the path. |
| 44 | L4 | depth=4 solutions=4 | Reject 3: already on the path. |
| 45 | L8 | depth=2 solutions=4 | Unchoose 3: back to [2]. |
| 46 | L8 | depth=1 solutions=4 | Unchoose 2: back to []. |
| 47 | L6 | depth=1 solutions=4 | Choose 3. |
| 48 | L1 | depth=2 solutions=4 | Explore [3]. |
| 49 | L6 | depth=2 solutions=4 | Choose 1. |
| 50 | L1 | depth=3 solutions=4 | Explore [3, 1]. |
| 51 | L4 | depth=4 solutions=4 | Reject 1: already on the path. |
| 52 | L6 | depth=3 solutions=4 | Choose 2. |
| 53 | L1 | depth=4 solutions=4 | Explore [3, 1, 2]. |
| 54 | L2 | depth=4 solutions=5 | Complete and valid: record [3, 1, 2]. |
| 55 | L8 | depth=3 solutions=5 | Unchoose 2: back to [3, 1]. |
| 56 | L4 | depth=4 solutions=5 | Reject 3: already on the path. |
| 57 | L8 | depth=2 solutions=5 | Unchoose 1: back to [3]. |
| 58 | L6 | depth=2 solutions=5 | Choose 2. |
| 59 | L1 | depth=3 solutions=5 | Explore [3, 2]. |
| 60 | L6 | depth=3 solutions=5 | Choose 1. |
| 61 | L1 | depth=4 solutions=5 | Explore [3, 2, 1]. |
| 62 | L2 | depth=4 solutions=6 | Complete and valid: record [3, 2, 1]. |
| 63 | L8 | depth=3 solutions=6 | Unchoose 1: back to [3, 2]. |
| 64 | L4 | depth=4 solutions=6 | Reject 2: already on the path. |
| 65 | L4 | depth=4 solutions=6 | Reject 3: already on the path. |
| 66 | L8 | depth=2 solutions=6 | Unchoose 2: back to [3]. |
| 67 | L4 | depth=3 solutions=6 | Reject 3: already on the path. |
| 68 | L8 | depth=1 solutions=6 | Unchoose 3: back to []. |
| 69 | L10 | solutions=6 nodes=31 return=[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] | Search finished: 6 solutions, 30 choices tried. Backtracking explores every branch the pruning rules allow. |
Combinations
A **combination** of size k is a subset with exactly k elements: C(n, k) = n! / (k!(n−k)!) of them. Generate them with the same start-index loop as subsets, but record a result only when the path reaches size k, and prune when too few values remain. C(5, 3) = 10.
Run the “k = 4 of 5” and “k = n” presets: C(5, 4) = 5 and C(3, 3) = 1. Combinations ignore order (1, 2 and 2, 1 are the same combination), which is why the loop moves forward only.
1static List<List<Integer>> choose(int n, int k) {2List<List<Integer>> out = new ArrayList<>();3pickK(n, k, 1, new ArrayList<>(), out);4return out;5}6private static void pickK(int n, int k, int start, List<Integer> path, List<List<Integer>> out) {7if (path.size() == k) { out.add(new ArrayList<>(path)); return; }8for (int v = start; v <= n - (k - path.size()) + 1; v++) { // stop when too few values remain9path.add(v);10pickK(n, k, v + 1, path, out);11path.remove(path.size() - 1);12}13}
Combinations of size k, with and without pruning
Interactive · edit the inputpartial solution
solutions found (0)
Choose 3 of 5 values in increasing order so each combination appears once. Pruning is on: a branch is abandoned as soon as the remaining values cannot fill the combination.
1static void combine(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out, boolean prune) {2if (path.size() == k) { out.add(new ArrayList<>(path)); return; }3for (int i = start; i < a.length; i++) {4if (prune && path.size() + (a.length - i) < k) break; // too few values left to reach size k5path.add(a[i]); // choose6combine(a, k, i + 1, path, out, prune); // explore7path.remove(path.size() - 1); // unchoose8}9}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 74 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | Choose 3 of 5 values in increasing order so each combination appears once. Pruning is on: a branch is abandoned as soon as the remaining values cannot fill the combination. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L5 | depth=1 solutions=0 | Choose 1. |
| 4 | L1 | depth=2 solutions=0 | Explore [1]. |
| 5 | L5 | depth=2 solutions=0 | Choose 2. |
| 6 | L1 | depth=3 solutions=0 | Explore [1, 2]. |
| 7 | L5 | depth=3 solutions=0 | Choose 3. |
| 8 | L1 | depth=4 solutions=0 | Explore [1, 2, 3]. |
| 9 | L2 | depth=4 solutions=1 | Complete and valid: record [1, 2, 3]. |
| 10 | L7 | depth=3 solutions=1 | Unchoose 3: back to [1, 2]. |
| 11 | L5 | depth=3 solutions=1 | Choose 4. |
| 12 | L1 | depth=4 solutions=1 | Explore [1, 2, 4]. |
| 13 | L2 | depth=4 solutions=2 | Complete and valid: record [1, 2, 4]. |
| 14 | L7 | depth=3 solutions=2 | Unchoose 4: back to [1, 2]. |
| 15 | L5 | depth=3 solutions=2 | Choose 5. |
| 16 | L1 | depth=4 solutions=2 | Explore [1, 2, 5]. |
| 17 | L2 | depth=4 solutions=3 | Complete and valid: record [1, 2, 5]. |
| 18 | L7 | depth=3 solutions=3 | Unchoose 5: back to [1, 2]. |
| 19 | L7 | depth=2 solutions=3 | Unchoose 2: back to [1]. |
| 20 | L5 | depth=2 solutions=3 | Choose 3. |
| 21 | L1 | depth=3 solutions=3 | Explore [1, 3]. |
| 22 | L5 | depth=3 solutions=3 | Choose 4. |
| 23 | L1 | depth=4 solutions=3 | Explore [1, 3, 4]. |
| 24 | L2 | depth=4 solutions=4 | Complete and valid: record [1, 3, 4]. |
| 25 | L7 | depth=3 solutions=4 | Unchoose 4: back to [1, 3]. |
| 26 | L5 | depth=3 solutions=4 | Choose 5. |
| 27 | L1 | depth=4 solutions=4 | Explore [1, 3, 5]. |
| 28 | L2 | depth=4 solutions=5 | Complete and valid: record [1, 3, 5]. |
| 29 | L7 | depth=3 solutions=5 | Unchoose 5: back to [1, 3]. |
| 30 | L7 | depth=2 solutions=5 | Unchoose 3: back to [1]. |
| 31 | L5 | depth=2 solutions=5 | Choose 4. |
| 32 | L1 | depth=3 solutions=5 | Explore [1, 4]. |
| 33 | L5 | depth=3 solutions=5 | Choose 5. |
| 34 | L1 | depth=4 solutions=5 | Explore [1, 4, 5]. |
| 35 | L2 | depth=4 solutions=6 | Complete and valid: record [1, 4, 5]. |
| 36 | L7 | depth=3 solutions=6 | Unchoose 5: back to [1, 4]. |
| 37 | L7 | depth=2 solutions=6 | Unchoose 4: back to [1]. |
| 38 | L4 | depth=3 solutions=6 | Reject 5: only 1 value left but 2 more are needed. |
| 39 | L7 | depth=1 solutions=6 | Unchoose 1: back to []. |
| 40 | L5 | depth=1 solutions=6 | Choose 2. |
| 41 | L1 | depth=2 solutions=6 | Explore [2]. |
| 42 | L5 | depth=2 solutions=6 | Choose 3. |
| 43 | L1 | depth=3 solutions=6 | Explore [2, 3]. |
| 44 | L5 | depth=3 solutions=6 | Choose 4. |
| 45 | L1 | depth=4 solutions=6 | Explore [2, 3, 4]. |
| 46 | L2 | depth=4 solutions=7 | Complete and valid: record [2, 3, 4]. |
| 47 | L7 | depth=3 solutions=7 | Unchoose 4: back to [2, 3]. |
| 48 | L5 | depth=3 solutions=7 | Choose 5. |
| 49 | L1 | depth=4 solutions=7 | Explore [2, 3, 5]. |
| 50 | L2 | depth=4 solutions=8 | Complete and valid: record [2, 3, 5]. |
| 51 | L7 | depth=3 solutions=8 | Unchoose 5: back to [2, 3]. |
| 52 | L7 | depth=2 solutions=8 | Unchoose 3: back to [2]. |
| 53 | L5 | depth=2 solutions=8 | Choose 4. |
| 54 | L1 | depth=3 solutions=8 | Explore [2, 4]. |
| 55 | L5 | depth=3 solutions=8 | Choose 5. |
| 56 | L1 | depth=4 solutions=8 | Explore [2, 4, 5]. |
| 57 | L2 | depth=4 solutions=9 | Complete and valid: record [2, 4, 5]. |
| 58 | L7 | depth=3 solutions=9 | Unchoose 5: back to [2, 4]. |
| 59 | L7 | depth=2 solutions=9 | Unchoose 4: back to [2]. |
| 60 | L4 | depth=3 solutions=9 | Reject 5: only 1 value left but 2 more are needed. |
| 61 | L7 | depth=1 solutions=9 | Unchoose 2: back to []. |
| 62 | L5 | depth=1 solutions=9 | Choose 3. |
| 63 | L1 | depth=2 solutions=9 | Explore [3]. |
| 64 | L5 | depth=2 solutions=9 | Choose 4. |
| 65 | L1 | depth=3 solutions=9 | Explore [3, 4]. |
| 66 | L5 | depth=3 solutions=9 | Choose 5. |
| 67 | L1 | depth=4 solutions=9 | Explore [3, 4, 5]. |
| 68 | L2 | depth=4 solutions=10 | Complete and valid: record [3, 4, 5]. |
| 69 | L7 | depth=3 solutions=10 | Unchoose 5: back to [3, 4]. |
| 70 | L7 | depth=2 solutions=10 | Unchoose 4: back to [3]. |
| 71 | L4 | depth=3 solutions=10 | Reject 5: only 1 value left but 2 more are needed. |
| 72 | L7 | depth=1 solutions=10 | Unchoose 3: back to []. |
| 73 | L4 | depth=2 solutions=10 | Reject 4: only 2 values left but 3 more are needed. |
| 74 | L9 | solutions=10 nodes=24 return=[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]] | Search finished: 10 solutions, 23 choices tried. Backtracking explores every branch the pruning rules allow. |
Practice ladder
Now try it yourself
Easy
- Subsets
- Letter Case Permutation
Medium
- Permutations II
- Combinations
- Subsets II
Advanced
- Permutation Sequence (the k-th permutation)
- Beautiful Arrangement
Key takeaways
- Subsets and combinations loop forward from a start index; permutations loop over all unused indices.
- Sort and skip equal neighbours to avoid duplicate results.
- Result sizes: 2^n subsets, C(n, k) combinations, n! permutations.