Permutations II — Medium Problem & Solution
nums may contain repeated values. Return every distinct ordering (permutation) of its elements — two orderings that read the same value by value count once,…
- Difficulty: Medium
- Topics: Arrays, Sorting, Backtracking
- Asked at: Microsoft, Meta, Bloomberg
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
nums may contain repeated values. Return every distinct ordering (permutation) of its elements — two orderings that read the same value by value count once, even if they place different copies of a repeated value.
List the permutations in lexicographic order (compare value by value from the left).
Example 1
Input: nums = [1,1,2]
Output: [[1,1,2],[1,2,1],[2,1,1]]
Example 2
Input: nums = [3,0,3,3]
Output: [[0,3,3,3],[3,0,3,3],[3,3,0,3],[3,3,3,0]]
Explanation: The three 3s are interchangeable, so only the position of the 0 matters.
Example 3
Input: nums = [2,-1,5]
Output: [[-1,2,5],[-1,5,2],[2,-1,5],[2,5,-1],[5,-1,2],[5,2,-1]]
Constraints
1 <= nums.length <= 8-10 <= nums[i] <= 10
How to solve Permutations II
Treat equal values as if they must be placed in index order. Every distinct permutation then has exactly one way to be built, and trying values in increasing order emits them lexicographically.
Approach
- Sort
numsand keep ausedflag per index. dfsfills the next position: for each unused indexi, skip it ifi > 0,nums[i] == nums[i - 1]andused[i - 1]is false.- Otherwise mark
i, appendnums[i], recurse, then undo both. - When the path has length
n, record a copy.
Why it works
The skip rule means the copies of one value are always consumed left to right, so two builds that differ only by swapping equal copies cannot both happen — each distinct sequence appears once. Positions are filled left to right and, at each position, candidate values are tried in increasing order, which is exactly lexicographic order of the finished sequences.
Complexity
- Time —
O(n · n!) in the worst case (all values distinct) - Space —
O(n) besides the output
Pitfalls
- Testing
used[i - 1]instead of!used[i - 1]also removes duplicates but emits the permutations in a different order — and is much slower. - Without sorting, equal values are not adjacent and duplicates slip through.
- Copy the path when recording it.
Reference solution
Python
from typing import List
def permuteUnique(nums: List[int]) -> List[List[int]]:
a = sorted(nums)
n = len(a)
used = [False] * n
out = []
path = []
def dfs():
if len(path) == n:
out.append(path[:])
return
for i in range(n):
if used[i]:
continue
if i > 0 and a[i] == a[i - 1] and not used[i - 1]:
continue
used[i] = True
path.append(a[i])
dfs()
path.pop()
used[i] = False
dfs()
return outJavaScript
var permuteUnique = function(nums) {
var a = nums.slice().sort(function(x, y) { return x - y; });
var n = a.length;
var used = new Array(n).fill(false);
var out = [];
var path = [];
var dfs = function() {
if (path.length === n) {
out.push(path.slice());
return;
}
for (var i = 0; i < n; i++) {
if (used[i]) continue;
if (i > 0 && a[i] === a[i - 1] && !used[i - 1]) continue;
used[i] = true;
path.push(a[i]);
dfs();
path.pop();
used[i] = false;
}
};
dfs();
return out;
};Also on the editorial tab: C, C#, C++, Go, Java, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 988 arrays problems · the whole catalogue
Learn the technique: Arrays · Sorting Algorithms