Permutations
Permutations
Category
Recursion
Difficulty
Medium
Problem Statement
Given an array of unique integers, return all possible permutations of the array. Each permutation must contain every element exactly once, and the order of permutations in the output does not matter.
Intuition
A permutation is an ordered arrangement of all elements. To build permutations, we can think of it as making a choice at each position: for the first position, we can pick any of the n elements; for the second, any of the remaining n-1; and so on. This naturally leads to a recursive backtracking approach where we swap elements into the current position and recurse on the rest.
Approach
- Use a recursive function that takes the array and a starting index.
- If the starting index equals the length of the array, we have a complete permutation — add a copy of the array to the results.
- Otherwise, for each index
ifromstartto the end of the array:- Swap the element at
startwith the element ati. - Recurse with
start + 1. - Swap back (backtrack) to restore the original order.
- Swap the element at
An alternative approach builds permutations by maintaining a "remaining" list and a "current permutation" list, choosing one element at a time from "remaining" and adding it to "current."
Pseudocode
function permutations(array):
results = []
helper(array, 0, results)
return results
function helper(array, start, results):
if start == length(array):
results.append(copy(array))
return
for i from start to length(array) - 1:
swap(array[start], array[i])
helper(array, start + 1, results)
swap(array[start], array[i])
Time & Space Complexity
- Time: O(n * n!) — there are n! permutations and creating each one takes O(n) work (copying the array).
- Space: O(n * n!) — to store all n! permutations, each of length n. The recursion call stack adds O(n) depth.
Key Insights
- Backtracking is essential: after exploring all permutations with a particular element at position
start, we swap it back so the next iteration can try a different element. - The swap-based approach modifies the array in place, which is more space-efficient during computation than creating new sub-arrays.
- The number of permutations grows factorially, so this problem is inherently expensive for large inputs.
- Every element must appear in every position across the full set of permutations.
Edge Cases
- Empty array
[]should return[[]](one permutation: the empty permutation). - Single-element array
[1]returns[[1]]. - Array with two elements
[1, 2]returns[[1, 2], [2, 1]]. - All elements are guaranteed unique, so no deduplication is needed. If duplicates were present, additional logic would be required to skip redundant swaps.