Given an array nums of unique integers, return all the possible permutations. You may return the answer in any order.
Example 1:
Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]Example 2:
Input: nums = [7]
Output: [[7]]Constraints:
1 <= nums.length <= 6-10 <= nums[i] <= 10
You should aim for a solution with O(n * n!) time and O(n) space, where n is the size of the input array.
A permutation is the same as the array but with the numbers arranged in a different order. The given array itself is also considered a permutation. This means we should make a decision at each step to take any element from the array that has not been chosen previously. By doing this recursively, we can generate all permutations. How do you implement it?
We can use backtracking to explore all possible permutation paths. We initialize a temporary list to append the chosen elements and a boolean array of size n (the same size as the input array) to track which elements have been picked so far (true means the element is chosen; otherwise, false). At each step of recursion, we iterate through the entire array, picking elements that have not been chosen previously, and proceed further along that path. Can you think of the base condition to terminate the current recursive path?
We observe that every permutation has the same size as the input array. Therefore, we can append a copy of the list of chosen elements in the current path to the result list if the size of the list equals the size of the input array terminating the current recursive path.
The idea is to generate permutations by building them from smaller permutations.
[].[2,3] and new number = 1,[1,2,3], [2,1,3], [2,3,1].This works because inserting the new number in all positions ensures we build all unique permutations.
nums is empty → return [[]].permute(nums[1:]) to get permutations of the smaller list.nums[0] at every position:0 to len(permutation)We build permutations step-by-step using iteration instead of recursion.
Start with one empty permutation: [].
For each number in nums, we take all existing permutations and insert the new number into every possible position.
Example building process for [1,2,3]:
[]1 → [1]2 into every position of [1] → [2,1], [1,2]3 into every position of each permutation:[2,1] → [3,2,1], [2,3,1], [2,1,3][1,2] → [3,1,2], [1,3,2], [1,2,3]By inserting each number in all positions of all existing permutations, we generate all possible permutations.
perms = [[]].num in nums:new_perms.p in perms:num into every index 0..len(p) to create new permutations.new_perms.perms with new_perms.perms as the final list of all permutations.Backtracking builds permutations by choosing numbers one-by-one and exploring all possible orders.
At every step:
We use a pick array to mark which elements are already used, ensuring each number appears only once per permutation.
This method explores a decision tree where each level chooses the next number until all numbers are used.
perm: the current permutation being built.pick[i]: whether nums[i] is already used.res: list of all completed permutations.len(perm) == len(nums), add a copy to res.i:pick[i] is false:pick[i] as true.nums[i] to perm.nums[i] and mark pick[i] as false.res.class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
self.res = []
self.backtrack([], nums, [False] * len(nums))
return self.res
def backtrack(self, perm: List[int], nums: List[int], pick: List[bool]):
if len(perm) == len(nums):
self.res.append(perm[:])
return
for i in range(len(nums)):
if not pick[i]:
perm.append(nums[i])
pick[i] = True
self.backtrack(perm, nums, pick)
perm.pop()
pick[i] = FalseWe want to generate all permutations, but instead of using a boolean pick array,
we use a bitmask (mask) to track which elements in nums have been used.
mask represents whether an index i is used.mask = 0101 means indices 0 and 2 are already chosen.(mask & (1 << i)) → checks if index i is used.(mask | (1 << i)) → marks index i as used for the next recursive call.We build permutations by trying every unused index at each step until we've chosen all numbers.
permmask = 0 meaning nothing is used yetperm has length equal to nums, add a copy to the result.i in nums:i in mask is 0 → number not used:nums[i] to permmask updated to mark i as usedpermclass Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
self.res = []
self.backtrack([], nums, 0)
return self.res
def backtrack(self, perm: List[int], nums: List[int], mask: int):
if len(perm) == len(nums):
self.res.append(perm[:])
return
for i in range(len(nums)):
if not (mask & (1 << i)):
perm.append(nums[i])
self.backtrack(perm, nums, mask | (1 << i))
perm.pop()This approach generates permutations in-place by swapping elements.
Instead of creating new lists or tracking visited elements, we treat the array as divided into:
0 to idx - 1)idx to end)At each step:
idx.i ≥ idx with idx.idx, we recursively fill the next index.This gives all permutations efficiently and uses O(1) extra space (besides recursion).
idx = 0.idx == len(nums), we have a full permutation → append a copy to result.i from idx to end:nums[idx] and nums[i]nums[i] in the current position)idx + 1class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
self.res = []
self.backtrack(nums, 0)
return self.res
def backtrack(self, nums: List[int], idx: int):
if idx == len(nums):
self.res.append(nums[:])
return
for i in range(idx, len(nums)):
nums[idx], nums[i] = nums[i], nums[idx]
self.backtrack(nums, idx + 1)
nums[idx], nums[i] = nums[i], nums[idx]When a complete permutation is found, you must add a copy of the current permutation list to the result. Adding the reference directly means all entries in the result will point to the same list, which gets modified during backtracking. Always use perm.copy(), new ArrayList<>(perm), or equivalent.
After recursively exploring with an element added to the permutation, you must remove it (backtrack) before trying the next element. Forgetting to pop the element or reset the visited flag results in incomplete exploration of the decision tree and missing permutations.
Using linear search to check if an element is already in the permutation leads to O(n) overhead per check, resulting in O(n! * n^2) time complexity. Using a boolean visited array or bitmask reduces this to O(1) per check, achieving the optimal O(n! * n) complexity.