Difficulty: Medium · LeetCode 46
Problem
Generate all permutations of distinct numbers.
Understanding the problem
Drawing tokens in different orders creates distinct permutations (n! arrangements). At each level, choose an unused element.
Intuition
Use a boolean used[] array to keep track of used elements in O(1) time instead of checking membership in current path.
Approach
Initialize
used = [False] * len(nums).Define
backtrack(current).If
len(current) == len(nums), append copy toresult.Loop
ifrom0tolen(nums) - 1. Ifused[i], skip.Set
used[i] = True, appendnums[i], recurse, pop, and setused[i] = False.
Naive Solution
At every level, scan the entire array and choose an unused element using if num in current, which costs O(n) per check.
class Solution:
def permute(self, nums):
result = []
def backtrack(current):
if len(current) == len(nums):
result.append(current[:])
return
for num in nums:
if num in current:
continue
current.append(num)
backtrack(current)
current.pop()
backtrack([])
return resultclass Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
vector<int> current;
backtrack(nums, current, result);
return result;
}
private:
void backtrack(vector<int>& nums, vector<int>& current, vector<vector<int>>& result) {
if (current.size() == nums.size()) {
result.push_back(current);
return;
}
for (int num : nums) {
if (find(current.begin(), current.end(), num) != current.end()) continue;
current.push_back(num);
backtrack(nums, current, result);
current.pop_back();
}
}
};class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
backtrack(nums, new ArrayList<>(), result);
return result;
}
private void backtrack(int[] nums, List<Integer> current, List<List<Integer>> result) {
if (current.size() == nums.length) {
result.add(new ArrayList<>(current));
return;
}
for (int num : nums) {
if (current.contains(num)) continue;
current.add(num);
backtrack(nums, current, result);
current.remove(current.size() - 1);
}
}
}var permute = function(nums) {
const result = [];
const backtrack = (current) => {
if (current.length === nums.length) {
result.push([...current]);
return;
}
for (const num of nums) {
if (current.includes(num)) continue;
current.push(num);
backtrack(current);
current.pop();
}
};
backtrack([]);
return result;
};Time: O(n × n!) · Aux Space: O(n) · Output Space: O(n × n!)
Code (Optimized)
class Solution:
def permute(self, nums):
result = []
used = [False] * len(nums)
def backtrack(current):
if len(current) == len(nums):
result.append(current[:])
return
for i in range(len(nums)):
if used[i]:
continue
used[i] = True
current.append(nums[i])
backtrack(current)
current.pop()
used[i] = False
backtrack([])
return resultclass Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
vector<int> current;
vector<bool> used(nums.size(), false);
backtrack(nums, used, current, result);
return result;
}
private:
void backtrack(vector<int>& nums, vector<bool>& used, vector<int>& current,
vector<vector<int>>& result) {
if (current.size() == nums.size()) {
result.push_back(current);
return;
}
for (int i = 0; i < (int)nums.size(); i++) {
if (used[i]) continue;
used[i] = true;
current.push_back(nums[i]);
backtrack(nums, used, current, result);
current.pop_back();
used[i] = false;
}
}
};class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
boolean[] used = new boolean[nums.length];
backtrack(nums, used, new ArrayList<>(), result);
return result;
}
private void backtrack(int[] nums, boolean[] used, List<Integer> current,
List<List<Integer>> result) {
if (current.size() == nums.length) {
result.add(new ArrayList<>(current));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) continue;
used[i] = true;
current.add(nums[i]);
backtrack(nums, used, current, result);
current.remove(current.size() - 1);
used[i] = false;
}
}
}var permute = function(nums) {
const result = [];
const used = new Array(nums.length).fill(false);
const backtrack = (current) => {
if (current.length === nums.length) {
result.push([...current]);
return;
}
for (let i = 0; i < nums.length; i++) {
if (used[i]) continue;
used[i] = true;
current.push(nums[i]);
backtrack(current);
current.pop();
used[i] = false;
}
};
backtrack([]);
return result;
};Time: O(n × n!) · Aux Space: O(n) · Output Space: O(n × n!)
Takeaway
Although the asymptotic time remains O(n × n!), the optimized solution removes the repeated O(n) membership search.