🧠
Backtracking with In-Place Swapping
💡 This approach avoids extra space for used arrays by swapping elements in place, which is more space efficient and often preferred in interviews. It also helps understand how to manipulate the input array directly to generate permutations.
Intuition
Fix one element at a time by swapping it with each candidate element, then recurse on the remaining elements.
Algorithm
- Start from index 0, swap it with every index from current to end.
- Recurse by fixing the next index.
- When index reaches array length, add current permutation to results.
- Backtrack by swapping back to restore original order.
💡 Swapping in place avoids extra bookkeeping and makes the recursion cleaner but requires careful swap-back to avoid corrupting input.
from typing import List
def permute(nums: List[int]) -> List[List[int]]:
res = []
def backtrack(start=0):
if start == len(nums):
res.append(nums[:])
return
for i in range(start, len(nums)):
nums[start], nums[i] = nums[i], nums[start]
backtrack(start + 1)
nums[start], nums[i] = nums[i], nums[start]
backtrack()
return res
# Driver code
if __name__ == '__main__':
print(permute([1,2,3]))
Line Notes
def backtrack(start=0)Start index indicates the position to fix in the permutation
if start == len(nums)Base case: all positions fixed, add a copy of current permutation
for i in range(start, len(nums))Try swapping current index with each candidate index
nums[start], nums[i] = nums[i], nums[start]Swap back to restore original order for next iteration
backtrack(start + 1)Recurse to fix next position
res.append(nums[:])Add a copy of current permutation to results
import java.util.*;
public class Permutations {
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
backtrack(nums, 0, res);
return res;
}
private static void backtrack(int[] nums, int start, List<List<Integer>> res) {
if (start == nums.length) {
List<Integer> permutation = new ArrayList<>();
for (int num : nums) permutation.add(num);
res.add(permutation);
return;
}
for (int i = start; i < nums.length; i++) {
swap(nums, start, i);
backtrack(nums, start + 1, res);
swap(nums, start, i);
}
}
private static void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
System.out.println(permute(nums));
}
}
Line Notes
backtrack(nums, 0, res)Start recursion fixing elements from index 0
if (start == nums.length)Base case: all elements fixed, add current permutation
for (int i = start; i < nums.length; i++)Try swapping current index with each candidate index
swap(nums, start, i)Swap back to restore original order for next iteration
backtrack(nums, start + 1, res)Recurse to fix next position
res.add(permutation)Add a copy of current permutation to results
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
backtrack(nums, 0, res);
return res;
}
private:
void backtrack(vector<int>& nums, int start, vector<vector<int>>& res) {
if (start == nums.size()) {
res.push_back(nums);
return;
}
for (int i = start; i < nums.size(); i++) {
swap(nums[start], nums[i]);
backtrack(nums, start + 1, res);
swap(nums[start], nums[i]);
}
}
};
int main() {
Solution sol;
vector<int> nums = {1, 2, 3};
vector<vector<int>> result = sol.permute(nums);
for (auto &perm : result) {
for (int num : perm) cout << num << ' ';
cout << '\n';
}
return 0;
}
Line Notes
backtrack(nums, 0, res)Begin recursion fixing elements starting at index 0
if (start == nums.size())Base case: all elements fixed, add current permutation
for (int i = start; i < nums.size(); i++)Try swapping current index with each candidate index
swap(nums[start], nums[i])Swap back to restore original order for next iteration
backtrack(nums, start + 1, res)Recurse to fix next position
res.push_back(nums)Add a copy of current permutation to results
function permute(nums) {
const res = [];
function backtrack(start = 0) {
if (start === nums.length) {
res.push(nums.slice());
return;
}
for (let i = start; i < nums.length; i++) {
[nums[start], nums[i]] = [nums[i], nums[start]];
backtrack(start + 1);
[nums[start], nums[i]] = [nums[i], nums[start]];
}
}
backtrack();
return res;
}
// Test
console.log(permute([1,2,3]));
Line Notes
function backtrack(start = 0)Start index indicates which position to fix next
if (start === nums.length)Base case: all positions fixed, add a copy of current permutation
for (let i = start; i < nums.length; i++)Try swapping current index with each candidate index
[nums[start], nums[i]] = [nums[i], nums[start]]Swap back to restore original order for next iteration
backtrack(start + 1)Recurse to fix next position
res.push(nums.slice())Add a copy of current permutation to results
TimeO(n! * n)
SpaceO(n) recursion stack + O(n! * n) output
Same factorial time complexity, but no extra used array space. Swapping is done in place.
💡 This approach saves space by modifying the input array directly, which is often preferred in interviews.
Interview Verdict: Accepted
This is the preferred approach in interviews due to its clarity and space efficiency.