Generate All Permutations of Array in DSA Typescript - Time & Space Complexity
When we generate all permutations of an array, we want to know how the time needed grows as the array gets bigger.
We ask: How does the number of steps change when the input size increases?
Analyze the time complexity of the following code snippet.
function permute(nums: number[]): number[][] {
const result: number[][] = [];
function backtrack(start: number) {
if (start === nums.length) {
result.push([...nums]);
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(0);
return result;
}
This code generates all possible orderings (permutations) of the input array by swapping elements and exploring all options recursively.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls combined with a loop that swaps elements to create permutations.
- How many times: The recursion explores every possible ordering, which is factorial in number of elements (n!).
Each time we add one more element, the number of permutations multiplies by that new number.
| Input Size (n) | Approx. Operations |
|---|---|
| 3 | 6 (3 x 2 x 1) |
| 5 | 120 (5 x 4 x 3 x 2 x 1) |
| 7 | 5,040 (7 x 6 x 5 x 4 x 3 x 2 x 1) |
Pattern observation: The number of operations grows very fast, multiplying by the input size each time.
Time Complexity: O(n × n!)
This means the time needed grows very fast because we explore every possible order of the array elements, and each permutation takes linear time to copy.
[X] Wrong: "Generating permutations is just like looping through the array once, so it should be O(n)."
[OK] Correct: Each element can be in many positions, creating many combinations. The total grows factorially, not linearly.
Understanding this complexity helps you explain why some problems take longer and shows you can reason about recursive solutions clearly.
"What if we only generated permutations of half the array? How would the time complexity change?"
Practice
Solution
Step 1: Understand permutations
Permutations are all possible ways to arrange elements in order.Step 2: Apply to array
Generating all permutations means listing every possible order of the array elements.Final Answer:
Listing all possible orders of the array elements -> Option AQuick Check:
Permutations = all orders [OK]
- Confusing permutations with sorting
- Thinking permutations remove duplicates
- Mixing permutations with finding max element
Solution
Step 1: Analyze input and output types
The input is an array of numbers, so type is number[]. The output is all permutations, which is an array of arrays of numbers, so number[][].Step 2: Match function signature
function permute(arr: number[]): number[][] matches input number[] and output number[][] correctly.Final Answer:
function permute(arr: number[]): number[][] -> Option CQuick Check:
Input array, output array of arrays [OK]
- Using single number instead of array as input
- Returning a single array instead of array of arrays
- Mixing string and number types incorrectly
function permute(arr: number[]): number[][] {
if (arr.length === 0) return [[]];
const result: number[][] = [];
for (let i = 0; i < arr.length; i++) {
const rest = [...arr.slice(0, i), ...arr.slice(i + 1)];
for (const perm of permute(rest)) {
result.push([arr[i], ...perm]);
}
}
return result;
}
console.log(permute([1, 2]));Solution
Step 1: Understand base case and recursion
Base case returns [[]] for empty array. For [1,2], it picks each element and permutes the rest.Step 2: Trace recursive calls for [1,2]
First i=0: arr[i]=1, rest=[2], permute([2]) returns [[2]], so push [1,2]. Then i=1: arr[i]=2, rest=[1], permute([1]) returns [[1]], so push [2,1].Final Answer:
[[1, 2], [2, 1]] -> Option AQuick Check:
Permutations of [1,2] = [[1,2],[2,1]] [OK]
- Forgetting to include all permutations
- Returning only one permutation
- Confusing order of elements
function permute(arr: number[]): number[][] {
if (arr.length === 1) return [arr];
const result: number[][] = [];
for (let i = 0; i < arr.length; i++) {
const rest = arr.slice(1);
for (const perm of permute(rest)) {
result.push([arr[i], ...perm]);
}
}
return result;
}Solution
Step 1: Analyze the slice operation
The code uses arr.slice(1) always, which removes only the first element, not the current element at index i.Step 2: Correct slice to exclude current element
To generate permutations correctly, slice should exclude arr[i], e.g., [...arr.slice(0, i), ...arr.slice(i + 1)].Final Answer:
The slice should exclude the current element, not always the first -> Option DQuick Check:
Exclude current element in slice [OK]
- Always slicing from index 1 ignoring i
- Wrong base case length check
- Returning wrong data structure
function permuteUnique(arr: number[]): number[][] {
// Your code here
}Solution
Step 1: Understand duplicate permutations problem
Duplicates in input cause repeated permutations if not handled.Step 2: Sort and skip duplicates during recursion
Sorting helps group duplicates. During recursion, skip elements equal to previous to avoid repeated branches.Step 3: Why other options fail
Using Set after generation is costly. Removing duplicates loses valid permutations. No change causes duplicates.Final Answer:
Sort the array first and skip duplicates during recursion -> Option BQuick Check:
Sort + skip duplicates = unique permutations [OK]
- Removing duplicates from input loses valid permutations
- Using Set after generation wastes time and memory
- Ignoring duplicates causes repeated permutations
Ready to prove it?
Pass the test and this pattern moves unknown
