Permutations
Problem
nums of distinct integers, return every possible
ordering (permutation) of its elements. The orderings may be returned in any order.[1,2,3]Output[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]][0,1]Output[[0,1],[1,0]][1]Output[[1]]1 ≤ n ≤ 6 · -10 ≤ nums[i] ≤ 10 · all values distinct
Try it yourself
class Solution {
public List<List<Integer>> permute(int[] nums) {
}
}
boolean[] used, recurse, then undo the
choice — the decision tree has exactly n! leaves.Instinct Return ALL combinations, permutations, partitions or placements - or ask if any arrangement works - build a candidate step by step and undo each choice.
Memory hack Choose, explore, un-choose - walk the decision tree and snip a branch the moment it can no longer succeed.
Open the Backtracking guide →Approaches
Brute ForceAll nn Index Sequences, Then Filter
O(n · nn)Space O(n) extraFill each of the n slots with any index (repeats allowed),
giving nn sequences; keep only those where all indices are
distinct. Correct, but most sequences are thrown away (e.g. 46,656 vs 720 for
n=6).
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> res = new ArrayList<>(); generate(nums, new int[nums.length], 0, res); return res; } private void generate(int[] nums, int[] idx, int pos, List<List<Integer>> res) { int n = nums.length; if (pos == n) { boolean[] seen = new boolean[n]; List<Integer> perm = new ArrayList<>(); for (int i : idx) { if (seen[i]) return; // repeated index -> not a permutation seen[i] = true; perm.add(nums[i]); } res.add(perm); return; } for (int i = 0; i < n; i++) { idx[pos] = i; generate(nums, idx, pos + 1, res); } }
Simple generate-and-test, but nn grows far faster than n!.
BetterIterative Insertion
O(n · n!)Space O(n!) extraStart from [[]]. For each new number, insert it into every
gap (size+1 positions) of every permutation built so far. After
k numbers there are k! lists, all distinct because the inserted positions differ.
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> res = new ArrayList<>(); res.add(new ArrayList<>()); for (int num : nums) { List<List<Integer>> next = new ArrayList<>(); for (List<Integer> perm : res) { for (int i = 0; i <= perm.size(); i++) { List<Integer> np = new ArrayList<>(perm); np.add(i, num); // insert into gap i next.add(np); } } res = next; } return res; }
No wasted sequences and no recursion, but copies whole intermediate levels.
OptimalBacktracking with used[]
O(n · n!)Space O(n) extraBuild one path. At each depth, try every index not yet
used, mark it, recurse, then unmark and pop. Only valid prefixes are
ever extended, so there are exactly n! leaves, each copied in O(n).
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> res = new ArrayList<>(); backtrack(nums, new ArrayList<>(), new boolean[nums.length], res); return res; } private void backtrack(int[] nums, List<Integer> path, boolean[] used, List<List<Integer>> res) { if (path.size() == nums.length) { res.add(new ArrayList<>(path)); return; } for (int i = 0; i < nums.length; i++) { if (used[i]) continue; used[i] = true; path.add(nums[i]); backtrack(nums, path, used, res); path.remove(path.size() - 1); // undo used[i] = false; } }
Output-optimal with O(n) extra space (excluding output) — the standard template.
nums[i] when it equals nums[i-1] and
!used[i-1].At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | All nn Index Sequences, Then Filter | O(n · nn) | O(n) extra |
| Better | Iterative Insertion | O(n · n!) | O(n!) extra |
| Optimal | Backtracking with used[] | O(n · n!) | O(n) extra |
- Adding
pathitself instead of a copy — every result ends up empty. - Forgetting to reset
used[i] = falseafter recursing. path.remove(x)with anIntegerremoves by value; pop by index.
- Draw the decision tree: n choices, then n-1, ... → n! leaves.
- State complexity as O(n · n!) — the copy at each leaf costs O(n).
- Mention the swap-in-place variant, which avoids the
usedarray.