A clear explanation of finding the lexicographically largest permutation smaller than the given array using at most one swap.
Problem Restatement
We are given an array of positive integers arr.
We need to return the lexicographically largest permutation smaller than arr that can be obtained with at most one swap.
If not possible, return arr.
The official constraints state that 1 <= arr.length <= 10^4 and 1 <= arr[i] <= 10^4.
Input and Output
| Item | Meaning |
|---|---|
| Input | Array arr |
| Output | Previous permutation with at most one swap |
Function shape:
def prevPermOpt1(arr: list[int]) -> list[int]:
...Examples
Example 1:
arr = [3, 2, 1]Previous: [3, 1, 2]. One swap of index 1 and 2.
Answer:
[3, 1, 2]Example 2:
arr = [1, 1, 5]For arr = [1, 1, 5], the array is already in nondecreasing order. No single swap can produce a lexicographically smaller permutation, so we return the array unchanged.
Answer for [1, 1, 5]:
[1, 1, 5]Key Insight
To get the previous permutation with one swap:
- Find the rightmost position
iwherearr[i] > arr[i+1](a descent). If none, already the smallest. - Find the largest
arr[j]to the right ofithat is less thanarr[i]. Among equal values, pick the leftmost (to avoid duplicate swaps). - Swap
arr[i]witharr[j].
Correctness
We want the largest permutation smaller than arr. The rightmost descent is the best place to make a swap (affects only the suffix). Swapping with the largest value smaller than arr[i] to its right gives the lexicographically largest decrease.
Edge Cases
- Check the minimum input size allowed by the constraints.
- Verify duplicate values or tie cases if the input can contain them.
- Keep the return value aligned with the exact failure case in the statement.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Two linear scans |
| Space | O(1) | In-place |
Common Pitfalls
- Do not optimize away the invariant; the code should still make it clear what condition is being maintained.
- Prefer problem-specific names over one-letter variables once the logic becomes stateful.
Implementation
class Solution:
def prevPermOpt1(self, arr: list[int]) -> list[int]:
n = len(arr)
i = n - 2
while i >= 0 and arr[i] <= arr[i + 1]:
i -= 1
if i < 0:
return arr
j = n - 1
while arr[j] >= arr[i]:
j -= 1
while j > 0 and arr[j] == arr[j - 1]:
j -= 1
arr[i], arr[j] = arr[j], arr[i]
return arrTesting
def run_tests():
s = Solution()
assert s.prevPermOpt1([3,2,1]) == [3,1,2]
assert s.prevPermOpt1([1,1,5]) == [1,1,5]
assert s.prevPermOpt1([1,9,4,6,7]) == [1,7,4,6,9]
assert s.prevPermOpt1([3,1,1,3]) == [1,3,1,3]
print("all tests passed")
run_tests()| Test | Expected | Why |
|---|---|---|
[3,2,1] | [3,1,2] | Swap last two |
[1,1,5] | [1,1,5] | Already minimum |
[1,9,4,6,7] | [1,7,4,6,9] | Swap 9 with 7 |
[3,1,1,3] | [1,3,1,3] | Rightmost descent found |