Prerequisites

Before attempting this problem, you should be comfortable with:

  • Array Manipulation - In-place swapping and reversing elements
  • Two Pointers Technique - Using left and right pointers to reverse subarrays
  • Lexicographic Ordering - Understanding how sequences are compared and ordered

1. Brute Force

Intuition

The most straightforward approach generates all permutations of the array, sorts them in lexicographic order, finds the current permutation in that sorted list, and returns the next one. If we are at the last permutation, we wrap around to the first.

This approach is extremely inefficient for large arrays since the number of permutations is n!, but it demonstrates the concept of what "next permutation" means.

Algorithm

  1. Generate all unique permutations of the input array.
  2. Sort the permutations lexicographically.
  3. Find the current array in the sorted list.
  4. Return the next permutation in the list (wrapping to the first if at the end).
  5. Copy the result back into the original array.
class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        permutations = self.permute(nums[:])
        permutations.sort()
        for i, p in enumerate(permutations):
            if p == nums:
                nextP = permutations[(i + 1) % len(permutations)]
                for j in range(len(nums)):
                    nums[j] = nextP[j]
                break


    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []

        def dfs(i):
            if i == len(nums):
                res.append(nums.copy())
                return

            for j in range(i, len(nums)):
                if j > i and nums[i] == nums[j]:
                    continue

                nums[i], nums[j] = nums[j], nums[i]
                dfs(i + 1)

            for j in range(len(nums) - 1, i, -1):
                nums[j], nums[i] = nums[i], nums[j]

        nums.sort()
        dfs(0)
        return res

Time & Space Complexity

  • Time complexity: O(n!n)O(n! * n)
  • Space complexity: O(n!n)O(n! * n)

2. Greedy

Intuition

To find the next lexicographically greater permutation, we need to make the smallest possible change that increases the array's value. The key insight is to find the rightmost position where we can make an increase.

Scan from right to left to find the first element that is smaller than its right neighbor. This element (call it pivot) can be swapped with something larger to get a bigger permutation. We then find the smallest element to the right of pivot that is still larger than pivot and swap them.

After the swap, everything to the right of the pivot position is in descending order. To get the smallest possible permutation from this point, we reverse that suffix to ascending order.

If no such pivot exists (the array is fully descending), we are at the largest permutation, so we reverse the entire array to get the smallest.

Algorithm

  1. Scan from the second-to-last element leftward to find the first index i where nums[i] < nums[i+1].
  2. If such an index exists:
    • Scan from the right to find the first index j where nums[j] > nums[i].
    • Swap nums[i] and nums[j].
  3. Reverse the subarray from i+1 to the end.
class Solution:
    def nextPermutation(self, nums: list[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        i = n - 2
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1

        if i >= 0:
            j = n - 1
            while nums[j] <= nums[i]:
                j -= 1
            nums[i], nums[j] = nums[j], nums[i]

        l, r = i + 1, n - 1
        while l < r:
            nums[l], nums[r] = nums[r], nums[l]
            l += 1
            r -= 1

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(1)O(1)

Common Pitfalls

Finding Pivot with Wrong Comparison

The pivot is the rightmost element that is smaller than its right neighbor (nums[i] < nums[i+1]). Using <= instead of < causes the algorithm to fail on arrays with duplicate elements, as it may skip valid pivot positions.

Swapping with Wrong Element

After finding the pivot, you must swap it with the smallest element to its right that is still larger than the pivot. Swapping with the first larger element from the left (instead of from the right) may not produce the lexicographically smallest increase.

Forgetting to Reverse the Suffix

After swapping, the suffix to the right of the pivot position is in descending order. Failing to reverse it to ascending order produces a larger permutation than necessary. The reversal is essential to get the next (smallest) greater permutation.