Data Structures · Greedy & Arrays · A Field Guide

The next
permutation

Reordering a sequence into the very next one in dictionary order. Not by listing all N! arrangements — by making the smallest possible change as far to the right as it can go.

127431 → 131247
01 · Strip it down

Rearrange into the next arrangement in dictionary order

List every permutation and sort them lexicographically; "next permutation" is the one right after the current array. [1,2,3] → [1,3,2], [2,3,1] → [3,1,2]. If the array is already the largest ([3,2,1]), wrap around to the smallest ([1,2,3]). Generating all N! arrangements is impossible even for N=100, and the constraint is in-place, O(1) space — so the next arrangement has to be derived directly.


02 · The governing idea

Smallest increase = change as far right as possible

Lexicographic order is dictionary order: compare left to right, and the first differing position decides everything. So a change at an early index causes a huge jump, a change at a late index a tiny one. To land on the very next arrangement, keep the longest possible prefix untouched and edit as far right as you can.

The three greedy moves 1. Find the rightmost spot that can still be increased — the pivot.  2. Bump it up by the smallest amount possible.  3. Make everything after it as small as possible.

It's the same instinct as incrementing an odometer: 129999 ticks the rightmost digits first, holding the high-order prefix as long as it can.


129

The odometer

A car's trip meter rolls the rightmost wheels first and only disturbs a higher wheel when the ones below max out. Next permutation is that instinct on unsorted digits.

The last uphill step

Walk backward down a descending slope until you hit the last rise. That rise is the pivot — the final place where a bigger step is still available before the tail runs downhill.

Flip the tail

The tail after the pivot is already stacked biggest-first (a maximal block). Once you nudge the pivot up, flip that block to smallest-first — no sorting needed, just a reverse.


04 · Walk the three moves

Pivot, swap, reverse

Set an array and step through. Phase 1 scans right-to-left, tinting the descending tail teal until it finds the pivot (the last rise). Phase 2 scans back for the smallest tail value greater than the pivot — the successor — and swaps. Phase 3 reverses the tail into ascending order.

Next-permutation machine

1 · Find pivot
2 · Swap successor
3 · Reverse tail

Try 3 2 1 (no pivot → whole array reverses) or 1 3 2 (why you can't just swap the last two).


05 · The mental picture

Prefix · pivot · maximal tail

Every array splits into three zones. The prefix stays frozen. The pivot is the one element that goes up. The tail after it is already descending — a maximal block — so it just needs flipping.

prefix (frozen)[1]
pivot ↑2
maximal tail[7, 4, 3, 1]

Choose the successor 3, swap it with the pivot, then minimize the tail:

prefix[1]
swapped ↑3
reversed tail[1, 2, 4, 7]

Final: [1, 3, 1, 2, 4, 7]. The prefix never moved, the pivot rose by the least it could, and the tail is as small as it can be — the definition of "next."


06 · Trace table

Three arrays, three shapes

Input Pivot Successor After swap Reverse tail Result
[1,2,3] i=1 (2) j=2 (3) [1,3,2] [2]→[2] [1,3,2]
[1,3,2] i=0 (1) j=2 (2) [2,3,1] [3,1]→[1,3] [2,1,3]
[3,2,1] none — — reverse all [1,2,3]

Row 2 is the instructive one: naively swapping the last two of [1,3,2] gives [1,2,3], which is smaller. The pivot is actually the 1, and reversing the tail afterward is what makes the result correct.


07 · The code

Three loops, one clean edge case

void nextPermutation(int[] nums) {
    int n = nums.length, i = n - 2;
    // 1. pivot: rightmost i with nums[i] < nums[i+1]
    while (i >= 0 && nums[i] >= nums[i + 1]) i--;
    if (i >= 0) {
        // 2. successor: rightmost value > pivot
        int j = n - 1;
        while (nums[j] <= nums[i]) j--;
        swap(nums, i, j);
    }
    // 3. reverse the tail (whole array if no pivot)
    reverse(nums, i + 1, n - 1);
}
Why reverse handles both cases
no pivot → i = −1
Then reverse(nums, 0, n−1) flips the whole descending array. No special branch needed.
Why the tail scan finds the smallest
tail is descending
The first value from the right that beats the pivot is automatically the smallest one that does.

O(N) time, O(1) space. Three sequential linear passes, all edits in-place. Sorting the tail would also work but costs O(N log N) — reversing exploits the structure that's already there.


08 · Traps to avoid

Five ways it slips

Scanning from the left

Changing the first place that can increase makes too big a jump. Next permutation preserves the longest prefix — scan right to left.

Swapping with the largest tail value

Pivot 2, tail [7,5,3,1] → swapping 7 overshoots. You want 3, the smallest value greater than the pivot.

Forgetting to reverse the tail

[1,3,2] pivot-swap gives [2,3,1]; stopping there is wrong. Minimizing the tail (→ [2,1,3]) is essential.

Using ≥ for the successor

The successor must be strictly greater — swapping with an equal value doesn't increase the permutation. Stop only when nums[j] > nums[i].

Sorting the whole array

That destroys the prefix you deliberately kept. Only [pivot+1 … n−1] may be rearranged.

Confusing it with rotation

[1,3,2] rotated is [2,1,3]-ish by luck, but ordering is lexicographic, not cyclic. Don't reach for rotations.


09 · Why it's correct

Three facts that force the answer

The invariant chain Before the pivot: the scanned tail is descending, so it's already the maximal arrangement of its values — nothing larger is possible while the prefix and pivot hold. After the swap: the prefix through the pivot is the smallest prefix strictly larger than the original. After the reverse: the tail is the smallest arrangement compatible with that new prefix.

Put together, the result is larger than the input (the pivot went up) and no valid permutation sits between them (the increase was minimal and the tail minimized). That's exactly "next." Duplicates don't break it — the strict > on the successor is what keeps the step honest.


10 · The pattern

Lexicographic greed: freeze, nudge, minimize

Array positions have descending importance — index 0 dominates everything after it. So the whole recipe is a priority game: preserve the high-priority prefix, make the smallest legal improvement, then minimize the low-priority remainder. The same three-beat move powers a family of problems.

Next greater arrangement / number
same pivot–swap–reverse
digits of an integer instead of an array
Smallest string / number after k swaps
freeze prefix, minimize tail
same lexicographic priority ordering

The transferable move: when a problem asks for the next or smallest lexicographic result, don't enumerate — find the rightmost decision point, change it minimally, and rebuild the tail as small as possible.


11 · One month from now

Reconstruct it, don't memorize it

  1. I need the smallest permutation larger than this one, so freeze as much left prefix as possible.
  2. Scan right-to-left past the descending tail; the last rise is the pivot — the final place I can go up.
  3. Swap the pivot with the smallest tail value strictly greater than it (first one hit scanning from the right).
  4. The tail is still descending, so reverse it to make it ascending — the smallest tail for the new prefix.
  5. No pivot at all? The array was the largest; reversing the whole thing wraps to the smallest.
The transferable insight For a lexicographically next result: preserve the longest possible prefix, make the smallest possible increase at the first changed position, and minimize everything after it. Understand that and next permutation stops being a trick — it becomes a derivable greedy algorithm.