Every Unique Ordering When Values May Repeat
Solve this ProblemTest Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ nums.length ≤ 7 - ◆
-10 ≤ nums[i] ≤ 10, and values may repeat - ◆
Results are returned sorted in ascending (lexicographic) order for a stable, checkable answer
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Generate Every Raw Arrangement, Then Deduplicate
BruteTreat every position as distinct regardless of its value and generate all n! raw arrangements exactly like the no-duplicates version. Repeated values then produce identical rows — sort every row into a canonical order, and walk through looking for consecutive equal rows to collapse away. Correct, but the full n! arrangements are always built first; if half of nums is one repeated value, most of that work is thrown away as duplicates immediately afterward.
O(n! · n)O(n! · n)1class Solution {
2 public int[][] uniquePermutationsOf(int[] nums) {
3 List<int[]> all = new ArrayList<>();
4 boolean[] used = new boolean[nums.length];
5 List<Integer> path = new ArrayList<>();
6 generateAll(nums, used, path, all);
7 all.sort((a, b) -> {
8 for (int i = 0; i < a.length; i++) {
9 if (a[i] != b[i]) return a[i] - b[i];
10 }
11 return 0;
12 });
13 List<int[]> result = new ArrayList<>();
14 for (int[] row : all) {
15 if (result.isEmpty() || !Arrays.equals(result.get(result.size() - 1), row)) {
16 result.add(row);
17 }
18 }
19 return result.toArray(new int[0][]);
20 }
21
22 private void generateAll(int[] nums, boolean[] used, List<Integer> path, List<int[]> all) {
23 if (path.size() == nums.length) {
24 int[] row = new int[path.size()];
25 for (int j = 0; j < row.length; j++) row[j] = path.get(j);
26 all.add(row);
27 return;
28 }
29 for (int i = 0; i < nums.length; i++) {
30 if (used[i]) continue;
31 used[i] = true;
32 path.add(nums[i]);
33 generateAll(nums, used, path, all);
34 path.remove(path.size() - 1);
35 used[i] = false;
36 }
37 }
38}Optimal — Sorted, Skip a Same-Level Sibling Still Unused
OptimalSort first so equal values sit next to each other, then build paths with a used-flags array as usual — but add one more check before trying an index: if it holds the same value as the index right before it, AND that earlier one currently isn't in use, skip it. "Not in use" is the key detail — it means that identical earlier value has already been backtracked past at this exact position, so trying this one now would only rebuild a path already produced. When the earlier identical value IS still in use (it's part of the path currently being extended, not a sibling choice), there's nothing to skip — that's a distinct, legitimate continuation. This generates every unique ordering exactly once, in lexicographic order, with no separate deduplication pass needed at all.
O(n!)O(n)1class Solution {
2 public int[][] uniquePermutationsOf(int[] nums) {
3 int[] sorted = nums.clone();
4 Arrays.sort(sorted);
5 List<int[]> result = new ArrayList<>();
6 boolean[] used = new boolean[sorted.length];
7 List<Integer> path = new ArrayList<>();
8 backtrack(sorted, used, path, result);
9 return result.toArray(new int[0][]);
10 }
11
12 private void backtrack(int[] sorted, boolean[] used, List<Integer> path, List<int[]> result) {
13 if (path.size() == sorted.length) {
14 int[] row = new int[path.size()];
15 for (int j = 0; j < row.length; j++) row[j] = path.get(j);
16 result.add(row);
17 return;
18 }
19 for (int i = 0; i < sorted.length; i++) {
20 if (used[i]) continue;
21 if (i > 0 && sorted[i] == sorted[i - 1] && !used[i - 1]) continue;
22 used[i] = true;
23 path.add(sorted[i]);
24 backtrack(sorted, used, path, result);
25 path.remove(path.size() - 1);
26 used[i] = false;
27 }
28 }
29}