Skip to content

Commit 5e9b9ba

Browse files
committed
refactor(algorithms, two-pointers): next permutation
1 parent dacbf8a commit 5e9b9ba

File tree

4 files changed

+107
-38
lines changed

4 files changed

+107
-38
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Next Permutation
2+
3+
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
4+
5+
If such an arrangement is not possible, it must rearrange it as the lowest possible order (i.e., sorted in ascending
6+
order).
7+
8+
The replacement must be in place and use only constant extra memory.
9+
10+
## Constraints
11+
12+
- 1 <= `nums.length` <= 100
13+
- 0 <= `nums[i]` <= 100
14+
15+
16+
```
17+
Example 1:
18+
19+
Input: nums = [1,2,3]
20+
Output: [1,3,2]
21+
```
22+
23+
```
24+
Example 2:
25+
26+
Input: nums = [3,2,1]
27+
Output: [1,2,3]
28+
```
29+
30+
```
31+
Example 3:
32+
33+
Input: nums = [1,1,5]
34+
Output: [1,5,1]
35+
```
36+
37+
```
38+
Example 4:
39+
40+
Input: nums = [1]
41+
Output: [1]
42+
```
43+
44+
## Solution
45+
46+
The problem asks us to find the next permutation of a given array of numbers. This is the next “dictionary order”
47+
(lexicographical) arrangement of the same numbers. We can solve this problem using a two pointers (or more accurately,
48+
a “two-index”) approach, as it allows us to efficiently find the two critical positions in the array that need to be
49+
changed.
50+
51+
We use one pointer (or index) to find the “pivot” element we need to increase, and a second pointer to find the
52+
“successor” element to swap it with. This targeted, two-index approach enables us to perform the minimal change required,
53+
which is crucial for finding the next permutation and satisfying the in-place, constant-memory constraints. To find the
54+
next smallest permutation that is larger than the current one, we need to make the smallest possible increase. This is
55+
done by modifying the “least significant” part of the array (the right-hand side) first. To do this, we make the
56+
smallest possible increase to the number, working from right to left:
57+
58+
1. **Find the pivot**: We scan from the right to find the first element (pivot) that is smaller than its right neighbor.
59+
This is the element we will increase.
60+
61+
2. **Find the successor**: We scan from the right again to find the smallest element (successor) that is larger than the
62+
pivot.
63+
64+
3. **Swap**: We swap the pivot and the successor.
65+
66+
4. **Reverse the suffix**: We reverse the part of the array to the right of the pivot’s original position. This ensures
67+
the new suffix is in its smallest possible order (ascending). This single reverse operation also cleverly handles
68+
both possible scenarios:
69+
- **Case 1 (pivot is found)**: The suffix (from i+1 onward) was previously in descending order. Reversing it sorts it
70+
into ascending order. This makes the new permutation as small as possible, ensuring it’s the immediate next one.
71+
- **Case 2 (no pivot is found)**: If the array were already in its largest order (e.g., [3,2,1]), the first loop
72+
would finish with i=−1. This final step will then reverse from i+1 (which is index 0) to the end, correctly
73+
transforming the entire array into its smallest possible order (e.g., .[1,2,3]).
74+
75+
Here’s a step-by-step breakdown of the code:
76+
77+
1. We initialize an index i to the second last element of nums.
78+
2. Next, we iterate backward starting from i to find the “pivot”. This is the rightmost element that can be changed to
79+
increase the permutation’s size.
80+
3. Then, we check if a pivot is actually found (i.e., i is greater than or equal to 0).
81+
- If a pivot is found, we initialize a second index, j, to the last element of nums.
82+
- Then, we iterate backward from j to find the “successor”. This is the smallest possible number in the suffix that
83+
we can swap with the pivot.
84+
- Once the “successor” is found, we swap the pivot nums[i] with its successor nums[j]. This guarantees the new
85+
permutation is larger than the original.
86+
4. Finally, we reverse the portion of the array that comes after the pivot’s original index i (i.e., from index i + 1
87+
to the end).
88+
89+
### Time Complexity
90+
91+
The time complexity of this solution is O(n), because in the worst-case scenario, we perform a single pass to find the
92+
pivot, another single pass to find the element to swap, and a final pass to reverse a part of the array.
93+
94+
### Space Complexity
95+
96+
The solution’s space complexity is O(1), as the permutation is done in-place, and only a constant amount of extra memory
97+
is used for variables.

puzzles/next_permutation/__init__.py renamed to algorithms/two_pointers/next_permutation/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,28 @@ def next_permutation(nums: List[int]) -> None:
55
"""
66
Does not return anything, modifies nums in-place instead.
77
"""
8+
# Start from the second last index (since we'll compare with the next element)
89
i = len(nums) - 2
910

11+
# Step 1: Find the first decreasing element from the end
12+
# This identifies the pivot point where the sequence stops increasing
1013
while i >= 0 and nums[i + 1] <= nums[i]:
1114
i -= 1
1215

16+
# Step 2: If such an element is found, find the next greater element to swap with
1317
if i >= 0:
1418
j = len(nums) - 1
15-
19+
# Move from the end to find the first element greater than nums[i]
1620
while nums[j] <= nums[i]:
1721
j -= 1
1822

23+
# Swap the pivot with the next greater element
1924
swap(nums, i, j)
25+
# Step 3: Reverse the suffix starting at i + 1 to get the smallest lexicographical order
2026
reverse(nums, i + 1)
2127

2228

29+
# Helper function to reverse a portion of the list
2330
def reverse(nums: List[int], start: int) -> None:
2431
i = start
2532
j = len(nums) - 1
@@ -30,6 +37,7 @@ def reverse(nums: List[int], start: int) -> None:
3037
j -= 1
3138

3239

40+
# Helper function to swap two elements in the list
3341
def swap(nums: List[int], i: int, j: int) -> None:
3442
temp = nums[i]
3543
nums[i] = nums[j]

puzzles/next_permutation/test_next_permutation.py renamed to algorithms/two_pointers/next_permutation/test_next_permutation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22

3-
from puzzles.next_permutation import next_permutation
3+
from algorithms.two_pointers.next_permutation import next_permutation
44

55

66
class NextPermutationTestCase(unittest.TestCase):

puzzles/next_permutation/README.md

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)