Next Permutation of Array in DSA Go - Time & Space Complexity
We want to understand how the time needed to find the next permutation changes as the array size grows.
How does the number of steps grow when the array gets bigger?
Analyze the time complexity of the following code snippet.
func nextPermutation(nums []int) {
n := len(nums)
i := n - 2
for i >= 0 && nums[i] >= nums[i+1] {
i--
}
if i >= 0 {
j := n - 1
for nums[j] <= nums[i] {
j--
}
nums[i], nums[j] = nums[j], nums[i]
}
reverse(nums[i+1:])
}
func reverse(nums []int) {
left, right := 0, len(nums)-1
for left < right {
nums[left], nums[right] = nums[right], nums[left]
left++
right--
}
}
This code finds the next lexicographical permutation of the array in place.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Scanning the array from the end to find the first decreasing element, then scanning again to find the element to swap, and finally reversing a subarray.
- How many times: Each loop can run up to n times in the worst case, where n is the array length.
As the array size grows, the number of steps grows roughly in a straight line with n.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to about 30 steps |
| 100 | Up to about 300 steps |
| 1000 | Up to about 3000 steps |
Pattern observation: The operations grow linearly with input size, meaning doubling the array roughly doubles the work.
Time Complexity: O(n)
This means the time to find the next permutation grows in direct proportion to the array size.
[X] Wrong: "The algorithm must check all permutations, so it takes factorial time."
[OK] Correct: The algorithm only scans the array a few times, never generating all permutations, so it runs much faster than factorial time.
Understanding this linear time approach shows you can handle array manipulations efficiently, a skill often needed in coding challenges and interviews.
"What if the array was sorted in descending order? How would the time complexity change?"
Practice
next permutation of an array represent?Solution
Step 1: Understand the concept of permutation order
Permutations can be ordered lexicographically like words in a dictionary.Step 2: Define next permutation
The next permutation is the next arrangement that is just larger than the current one in this order.Final Answer:
The next lexicographically greater arrangement of the array elements -> Option AQuick Check:
Next permutation = next bigger arrangement [OK]
- Confusing next permutation with sorting
- Thinking it always reverses the array
- Assuming it moves smallest element to front
nums[i] and nums[j] in an array?Solution
Step 1: Recall Go multiple assignment syntax
Go allows swapping two variables in one line using multiple assignment.Step 2: Identify correct swap syntax
The syntaxnums[i], nums[j] = nums[j], nums[i]swaps values correctly without temporary variable.Final Answer:
nums[i], nums[j] = nums[j], nums[i] -> Option CQuick Check:
Go swap uses multiple assignment [OK]
- Using undefined swap function
- Forgetting temporary variable in manual swap
- Assigning both to same value causing loss
nums := []int{1, 3, 2}?
func nextPermutation(nums []int) {
i := len(nums) - 2
for i >= 0 && nums[i] >= nums[i+1] {
i--
}
if i >= 0 {
j := len(nums) - 1
for nums[j] <= nums[i] {
j--
}
nums[i], nums[j] = nums[j], nums[i]
}
left, right := i+1, len(nums)-1
for left < right {
nums[left], nums[right] = nums[right], nums[left]
left++
right--
}
}
func main() {
nums := []int{1, 3, 2}
nextPermutation(nums)
fmt.Println(nums)
}Solution
Step 1: Find first decreasing element from right
Starting from index 1 (value 3), check if nums[i] >= nums[i+1]. At i=1, 3 >= 2 true, i=0, 1 >= 3 false, so i=0.Step 2: Find element just larger than nums[i]
From right, find j where nums[j] > nums[i] (nums[0]=1). j=2 (value 2) > 1, swap nums[0] and nums[2]. Array becomes [2,3,1]. Reverse suffix from i+1=1 to end: reverse [3,1] to [1,3]. Final array: [2,1,3].Final Answer:
[2, 1, 3] -> Option BQuick Check:
Next permutation of [1,3,2] = [2,1,3] [OK]
- Not reversing suffix after swap
- Swapping wrong elements
- Stopping at first decreasing element without swap
func nextPermutation(nums []int) {
i := len(nums) - 1
for i > 0 && nums[i] <= nums[i-1] {
i--
}
if i > 0 {
j := len(nums) - 1
for nums[j] <= nums[i-1] {
j--
}
nums[i-1], nums[j] = nums[j], nums[i-1]
}
left, right := i, len(nums)-1
for left < right {
nums[left], nums[right] = nums[right], nums[left]
left++
right--
}
}Solution
Step 1: Check starting index for finding first decreasing element
The algorithm must start checking from second last element (len(nums)-2) to compare with next element.Step 2: Identify impact of starting at len(nums)-1
Starting at len(nums)-1 compares last element with out-of-range index, causing incorrect behavior or skipping needed checks.Final Answer:
The initial index i should start at len(nums) - 2, not len(nums) - 1 -> Option AQuick Check:
Start i at len(nums)-2 for correct next permutation [OK]
- Starting i at last index causing index errors
- Swapping wrong elements
- Incorrect reversal range
nums := []int{3, 2, 1}, what will be the next permutation after applying the standard next permutation algorithm? Explain why.Solution
Step 1: Check if array is in descending order
Array [3, 2, 1] is strictly descending, meaning it is the highest permutation possible.Step 2: Apply next permutation rule for highest permutation
When highest permutation is reached, next permutation is the lowest ascending order, which is [1, 2, 3].Final Answer:
[1, 2, 3] because the array is in descending order, so next permutation is the lowest ascending order -> Option DQuick Check:
Descending array next permutation = ascending array [OK]
- Thinking next permutation is same as current if descending
- Swapping wrong elements instead of reversing whole array
- Not resetting to lowest permutation after highest
Ready to prove it?
Pass the test and this pattern moves unknown
