Array · 24 of 36
Next Permutation
Rearrange number cards into the very next bigger order, like turning a car odometer forward by the smallest possible step.
- Simple way
- time O(n! * n)
room O(n! * n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get an array of numbers. Think of every way to order those same numbers, sorted like words in a dictionary: compare the first number, then the second, and so on. Change the array, in place, into the order that comes right after it in that list.
If the array is already the last order (all numbers going down), wrap around to the first order: sorted going up.
A story to remember it
Picture it
Every order of 1, 2, 3 in dictionary order, read left to right along the bottom. The next order after 132 is the next leaf to its right: 213.
- looking now
- answer
Examples
A normal case
- In
[1, 3, 5, 4, 2]- Out
[1, 4, 2, 3, 5]
The tail 5, 4, 2 is maxed out. 3 can grow: swap it with 4, then flip the tail to 2, 3, 5.
A small case
- In
[1, 2, 3]- Out
[1, 3, 2]
Only the last two need to change.
The last order wraps around
- In
[3, 2, 1]- Out
[1, 2, 3]
Everything goes down, so there is no bigger order. Flip the whole array to the smallest order.
Repeated numbers
- In
[1, 5, 1]- Out
[5, 1, 1]
The dip is the first 1. The smallest number to its right that is strictly bigger is 5, not the other 1.
One number
- In
[7]- Out
[7]
There is only one order. It wraps to itself.
Way 1: the simple way
Make every order of the numbers, sort them, find the current one, and take the one after it (or the first one if the current is last).
class Solution {
public void nextPermutation(int[] nums) {
TreeSet<List<Integer>> all = new TreeSet<>((a, b) -> { // dictionary order
for (int i = 0; i < a.size(); i++) {
int c = Integer.compare(a.get(i), b.get(i));
if (c != 0) return c;
}
return 0;
});
List<Integer> now = new ArrayList<>();
for (int x : nums) now.add(x);
build(nums.clone(), 0, all);
List<Integer> next = all.higher(now);
if (next == null) next = all.first(); // the last order wraps around
for (int i = 0; i < nums.length; i++) nums[i] = next.get(i);
}
private void build(int[] a, int start, TreeSet<List<Integer>> all) {
if (start == a.length) {
List<Integer> p = new ArrayList<>();
for (int x : a) p.add(x);
all.add(p); // a set drops repeated orders
return;
}
for (int i = start; i < a.length; i++) {
int t = a[start]; a[start] = a[i]; a[i] = t;
build(a, start + 1, all);
t = a[start]; a[start] = a[i]; a[i] = t; // put it back
}
}
}How long it takes: there are n! orders and each one has n numbers, O(n! * n) (and more for keeping them sorted). Extra room: all of them are stored, O(n! * n). Already for 12 numbers that is hundreds of millions of orders.
Way 2: the clever way
- Find the dip. Walk
ifrom the second to last index to the left whilenums[i] >= nums[i + 1]. Stop at the firstnums[i] < nums[i + 1]. Everything right ofiis going down. - If there is no dip (
i < 0), the whole array goes down. Flip it all and stop. - Find the swap partner. Walk
jfrom the right end to the left whilenums[j] <= nums[i]. The firstnums[j]bigger thannums[i]is the smallest one that is bigger, because the tail goes down. - Swap
nums[i]andnums[j]. The tail still goes down. - Flip the tail (
i + 1to the end) so it goes up. That makes it the smallest possible tail.
Find the dip, swap, flip the tail
01 / 09
Start at the right end. We look for the first number, going left, that is smaller than its right neighbour.
- looking now
- checking
- answer
- just added
class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
int i = n - 2;
while (i >= 0 && nums[i] >= nums[i + 1]) i--; // find the dip
if (i >= 0) {
int j = n - 1;
while (nums[j] <= nums[i]) j--; // smallest bigger number in the tail
swap(nums, i, j);
}
reverse(nums, i + 1, n - 1); // make the tail as small as possible
}
private void swap(int[] a, int i, int j) {
int t = a[i]; a[i] = a[j]; a[j] = t;
}
private void reverse(int[] a, int lo, int hi) {
while (lo < hi) swap(a, lo++, hi--);
}
}How long it takes: at most three walks over the array, O(n). Extra room: a few indices, O(1). When there is no dip, i is -1 and the flip covers the whole array, which handles the wrap-around for free.
Watch out for
- Sorting the tail instead of flipping it works but costs
O(n log n). The tail always goes down, so flipping is enough. - Do not return a new array. The problem asks you to change
numsin place. - One element or an empty array:
istarts below 0, the flip does nothing, and that is correct.
What interviewers ask next
- Previous permutation. Mirror every step: find the first
nums[i] > nums[i + 1]from the right, swap with the biggest smaller number in the tail, then flip the tail. - The k-th permutation of
1..n. Do not stepktimes. Use the factorial number system: there are(n-1)!orders for each first number, so divide to pick the first number, then repeat. - Next bigger number with the same digits (for example 534976 gives 536479). It is exactly this algorithm on the digits. Return -1 if there is no dip or if the result does not fit in an
int. - Print all permutations in order. Start from the sorted array and call next permutation until it wraps around. This handles repeated numbers without a set.
Remember this
Grown-up words: an order of the numbers is a permutation, dictionary order is lexicographic order, the going-down tail is the longest non-increasing suffix, and the swap partner is the dip's successor.