下一个排列

解法:记住步骤

解题思路
不用思考,套用现成的方法
算法流程
我们希望下一个数 比当前数大,这样才满足 “下一个排列” 的定义。因此只需要 将后面的「大数」与前面的「小数」交换,就能得到一个更大的数。比如 123456,将 5 和 6 交换就能得到一个更大的数 123465。
我们还希望下一个数 增加的幅度尽可能的小,这样才满足“下一个排列与当前排列紧邻“的要求。为了满足这个要求,我们需要:
  • 尽可能靠右的低位 进行交换,需要 从后向前 查找
  • 将一个 尽可能小的「大数」 与前面的「小数」交换。比如 123465,下一个排列应该把 5 和 4 交换而不是把 6 和 4 交换
  • 将「大数」换到前面后,需要 将「大数」后面的所有数 重置为升序,升序排列就是最小的排列。以 123465 为例:首先按照上一步,交换 5 和 4,得到 123564;然后需要将 5 之后的数重置为升序,得到 123546。显然 123546 比 123564 更小,123546 就是 123465 的下一个排列
  • 以上就是求 “下一个排列” 的分析过程。
可视化
以求 12385764 的下一个排列为例
以求 12385764 的下一个排列为例
首先从后向前查找第一个相邻升序的元素对 (i,j)。这里 i=4,j=5,对应的值为 5,7
首先从后向前查找第一个相邻升序的元素对 (i,j)。这里 i=4j=5,对应的值为 57
然后在 [j,end) 从后向前查找第一个大于 A[i] 的值 A[k]。这里 A[i] 是 5,故 A[k] 是 6
然后在 [j,end) 从后向前查找第一个大于 A[i] 的值 A[k]。这里 A[i] 是 5,故 A[k] 是 6
将 A[i] 与 A[k] 交换。这里交换 5、6
将 A[i] 与 A[k] 交换。这里交换 56
这时 [j,end) 必然是降序,逆置 [j,end),使其升序。这里逆置 [7,5,4]
这时 [j,end) 必然是降序,逆置 [j,end),使其升序。这里逆置 [7,5,4]
因此,12385764 的下一个排列就是 12386457
notion image
代码
class Solution { public void nextPermutation(int[] nums) { int len = nums.length; if(len <= 1) return; int i = len - 2, j = len - 1, k = len - 1; // 倒着找到首个升序相邻元素对标记为i,j while(i >= 0 && nums[i] >= nums[j]) { i--; j--; } // 倒着找到j后第一个比i大的数标记为k,i,k交换 if(i >= 0) { while(nums[i] >= nums[k]) k--; swap(nums, i, k); } // reverse j后的元素 reverse(nums, j, len-1); } private void swap(int[] nums, int a, int b) { int temp = nums[a]; nums[a] = nums[b]; nums[b] = temp; } private void reverse(int[] nums, int start, int end) { while(start < end) { swap(nums, start, end); start++; end--; } } }

相似题