Next Permutation
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
A permutation of an array is any reordering of its elements. If we list all permutations of the array in lexicographic (dictionary) order, the next permutation of nums is the one that comes immediately after it in that list. Rearrange nums into its next permutation, in place, using only constant extra memory. If nums is already the last permutation (sorted in non-increasing order), wrap around to the first one (sorted in non-decreasing order).
Constraints: 1 ≤ n ≤ 100, 0 ≤ nums[i] ≤ 100 on LeetCode; the method works for any int values and duplicates are allowed.
Example 1
nums = [1, 2, 3]
answer = [1, 3, 2] // the permutations in order are 123, 132, 213, 231, 312, 321
Example 2
nums = [3, 2, 1]
answer = [1, 2, 3] // 321 is the last permutation, so wrap to the first
Example 3
nums = [1, 1, 5]
answer = [1, 5, 1] // duplicates are fine
Approach 1: Brute force, generate every permutation
Intuition. The definition talks about “the list of all permutations in order”, so the most literal solution is to build that list. Generate every distinct permutation, sort them lexicographically, find the input in the sorted list, and copy the one after it (wrapping to the first). Duplicates need care: [1, 1, 5] has only three distinct permutations, not six, and we must not list [1, 1, 5] twice or the “next” one would be itself. The generator below avoids that by never placing the same value at the same position twice.
Algorithm.
- Recursively generate all distinct permutations: for position
idx, try each distinct value found atidx..n-1, swap it in, recurse, swap it back. - Sort the generated permutations lexicographically.
- Locate the input, take the following permutation (index
(i + 1) mod size) and copy it intonums.
static void bruteForce(int[] nums) {
List<int[]> all = new ArrayList<>();
generate(nums.clone(), 0, all);
all.sort(P31_NextPermutation::compareLex);
for (int i = 0; i < all.size(); i++) {
if (Arrays.equals(all.get(i), nums)) {
int[] next = all.get((i + 1) % all.size()); // wrap around after the last permutation
System.arraycopy(next, 0, nums, 0, nums.length);
return;
}
}
}
private static void generate(int[] a, int idx, List<int[]> out) {
if (idx == a.length) {
out.add(a.clone());
return;
}
Set<Integer> used = new HashSet<>(); // a value placed at idx once must not be placed again
for (int i = idx; i < a.length; i++) {
if (!used.add(a[i])) continue;
swap(a, idx, i);
generate(a, idx + 1, out);
swap(a, idx, i);
}
}
private static int compareLex(int[] x, int[] y) {
for (int i = 0; i < x.length; i++) {
if (x[i] != y[i]) return Integer.compare(x[i], y[i]);
}
return 0;
}
Complexity. Time O(n! × n): there are up to n! permutations, each costs O(n) to copy and O(n) per comparison during the sort (O(n! log n!) comparisons). Space O(n! × n) to hold them all. For n = 10 that is 3.6 million arrays; for n = 15 it is hopeless. We need to find the next permutation without looking at the others.
Approach 2: Optimal, pivot, successor, reverse
Intuition. Think about how you would find the next word in a dictionary. You keep as long a prefix as possible unchanged and make the smallest possible change at the end. Look at the array from the right: as long as the suffix is non-increasing (nums[i] >= nums[i+1]), that suffix is already the largest arrangement of its own elements, so no rearrangement of it alone can produce a bigger permutation. The first position from the right where the sequence increases, nums[i] < nums[i+1], is the pivot: that is the rightmost position whose value can be bumped up.
To bump it up by the smallest possible amount, swap nums[i] with the smallest element to its right that is still larger than it. Because the suffix is non-increasing, scanning from the right and stopping at the first nums[j] > nums[i] finds exactly that element. After the swap the suffix is still non-increasing (we replaced a value with one just above it and put a value just below in its place), so it is still the largest arrangement of its elements. We want the smallest, so reverse it.
If no pivot exists, the whole array is non-increasing, meaning it is the last permutation; reversing the whole thing yields the first one, which is the required wrap-around.
Algorithm.
- Find the largest
isuch thatnums[i] < nums[i + 1]. If none exists, reverse the whole array and stop. - Find the largest
j > isuch thatnums[j] > nums[i], and swapnums[i]withnums[j]. - Reverse the suffix
nums[i + 1 .. n - 1].
static void optimal(int[] nums) {
int n = nums.length;
int pivot = -1;
for (int i = n - 2; i >= 0; i--) { // 1. rightmost i with nums[i] < nums[i + 1]
if (nums[i] < nums[i + 1]) {
pivot = i;
break;
}
}
if (pivot == -1) { // whole array is non-increasing: it is the last permutation
reverse(nums, 0, n - 1);
return;
}
for (int j = n - 1; j > pivot; j--) { // 2. rightmost j with nums[j] > nums[pivot]: the smallest value larger than the pivot
if (nums[j] > nums[pivot]) {
swap(nums, pivot, j);
break;
}
}
reverse(nums, pivot + 1, n - 1); // 3. suffix is still non-increasing; reversing makes it the smallest arrangement
}
private static void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void reverse(int[] a, int lo, int hi) {
while (lo < hi) swap(a, lo++, hi--);
}
Dry run on nums = [1, 5, 8, 4, 7, 6, 5, 3, 1]:
| Step | Observation | Array |
|---|---|---|
| find pivot | suffix 7, 6, 5, 3, 1 is non-increasing; 4 < 7 at index 3 |
pivot = 3 |
| find successor | from the right, first value > 4 is 5 at index 6 |
swap indices 3 and 6 |
| after swap | [1, 5, 8, 5, 7, 6, 4, 3, 1] |
|
| reverse suffix | indices 4..8 | [1, 5, 8, 5, 1, 3, 4, 6, 7] |
Why > and not >= when looking for the pivot? With duplicates, [1, 5, 1] has pivot at index 0 (1 < 5), and the suffix 5, 1 is non-increasing. If we used <= to detect the pivot we would stop at equal neighbours and swap two equal values, producing the same permutation again. Strict comparison keeps the result correct with repeated values, and [1, 1, 5] -> [1, 5, 1] in the tests exercises exactly this.
Complexity. Time O(n): each of the three steps is a single pass over at most n elements. Space O(1): the array is modified in place with a few index variables.
Edge cases and pitfalls
- Already the last permutation.
[3, 2, 1]has no pivot. Reverse everything to get[1, 2, 3]. Forgetting this case leaves the array unchanged, which is wrong. - Single element or empty. The pivot loop starts at
n - 2, which is negative, so it does nothing; the reverse of an empty or one-element range is a no-op. Both are handled without special code. - Duplicates. Use strict
<for the pivot and strict>for the successor.[2, 2, 2]has only one distinct permutation and correctly maps to itself. - Scanning direction for the successor. You must scan from the right to find the smallest value greater than the pivot. Scanning from the left would find the first larger value, which in a non-increasing suffix is the largest one, and the result would skip many permutations.
- Forgetting the reverse. After the swap the suffix is the largest arrangement of its elements. Without the reverse you get a larger permutation, but not the next one.
- Negative numbers. Nothing changes; comparisons are the same.
[-3, -1, -2] -> [-2, -3, -1]is in the tests. - Brute force and recursion depth. The generator recurses
nlevels deep, fine for smalln, but the number of permutations is the real limit: keep the brute force forn ≤ 9or so.
Related problems
- Rearrange array elements by sign is another rearrangement that relies on reading the array in a specific order.
- Leaders in an Array uses the same “scan from the right and remember the suffix property” pattern.
- Print the matrix in spiral manner is another problem where carefully chosen traversal order is the entire solution.