Files
hello-algo/en/docs/chapter_backtracking/permutations_problem.md
Yudong Jin 14c314c08c Retranslate the complete English edition from Simplified Chinese (#2010)
* Retranslate the complete English edition from Simplified Chinese

* Polish three English explanations after reader-focused review
2026-10-31 03:35:51 +08:00

5.4 KiB

Permutations problem

Generating permutations is a classic application of backtracking. Given a collection, such as an array or a string, the task is to find every possible ordering of its elements.

The table below gives several input arrays and all their permutations.

Table   Examples of permutations

Input array All permutations
[1] [1]
[1, 2] [1, 2], [2, 1]
[1, 2, 3] [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]

Distinct elements

!!! question

Given an integer array with no duplicate elements, return all possible permutations.

From a backtracking perspective, we can view a permutation as the result of a sequence of choices. For input [1, 2, 3], choosing 1, then 3, then 2 produces [1, 3, 2]. Backtracking undoes a choice so that other choices can be tried.

In the code, choices contains all the input elements, and state contains the elements chosen so far. Each element may be chosen only once, so every element in state must be unique.

As shown in the figure below, we can expand the search into a recursion tree, where each node represents the current state. Starting from the root, three rounds of choices lead to a leaf, and each leaf corresponds to one permutation.

Recursion tree for permutations

Pruning repeated selections

To ensure that each element is chosen only once, introduce a Boolean array selected, where selected[i] indicates whether choices[i] has been chosen. Use it for the following pruning steps.

  • After choosing choices[i], set selected[i] to \text{True} to mark it as selected.
  • When iterating over choices, skip all selected elements, pruning their branches.

As shown in the figure below, if we choose 1 first, 3 second, and 2 third, we must prune the branch for 1 in the second round and the branches for 1 and 3 in the third round.

Example of pruning permutations

This pruning reduces the search space from O(n^n) to O(n!).

Code implementation

With these details established, we can fill in the backtracking template. To keep the code short, we inline the template's helper functions into backtrack():

[file]{permutations_i}-[class]{}-[func]{permutations_i}

Duplicate elements

!!! question

Given an integer array that **may contain duplicate elements**, return all distinct permutations.

Suppose the input is [1, 1, 2]. To distinguish the two occurrences of 1, denote the second 1 by \hat{1}.

As shown in the figure below, half the permutations generated by the previous method are duplicates.

Duplicate permutations

How can we remove these duplicates? A straightforward approach is to use a hash set to deduplicate the results. However, this is inelegant: branches that generate duplicate permutations are unnecessary and should be identified and pruned in advance, improving efficiency.

Pruning equal elements

In the figure below, choosing 1 or \hat{1} in the first round is equivalent: every permutation produced under one choice is duplicated under the other. We should therefore prune \hat{1}.

Similarly, after choosing 2 in the first round, choosing either 1 or \hat{1} in the second produces duplicate branches. Prune the second-round choice of \hat{1} as well.

In essence, within any one round of choices, we want to try only one of the elements with the same value.

Pruning duplicate permutations

Code implementation

Building on the previous solution, create a hash set duplicated in each round to record the values already tried in that round and prune repeated values:

[file]{permutations_ii}-[class]{}-[func]{permutations_ii}

If all elements are distinct, n elements have n! permutations, where the exclamation mark denotes factorial. Recording each result requires copying a list of length n, taking O(n) time. The time complexity is therefore $O(n!n)$.

The maximum recursion depth is n, requiring O(n) space for stack frames. The array selected uses O(n) space. At most n duplicated sets exist at once, using O(n^2) space. The space complexity is therefore $O(n^2)$.

Comparing the two pruning methods

Although both selected and duplicated are used for pruning, they serve different purposes.

  • Pruning repeated selections: There is only one selected array throughout the search. It records which elements are in the current state, preventing the same element from appearing twice in state.
  • Pruning equal elements: Each round of choices (each call to backtrack) has its own duplicated set. It records the values already chosen in that round's for loop, ensuring that equal elements are tried only once in that round.

The figure below shows the scope of each pruning condition. Each node represents a choice, and the nodes along a path from root to leaf form a permutation.

Scope of the two pruning conditions