(一)题目描述
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 1,2,3
→ 1,3,2
3,2,1
→ 1,2,3
1,1,5
→ 1,5,1
(二)算法思路
首先解释一下什么是字典序:
设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn
1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1}
2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)
3)对换pj,pk
4)再将pj+1......pk-1pkpk+1......pn倒转得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个排列。
解法:
解法一:我们可以把所有的排序全部列出来,然后找到下一排列,但是这样的算法效率太低,LeetCode肯定不能通过.但是这也是最简单和最容易想到的方法了.
解法二:一遍扫描法
- 我们直接从后往前扫描,先把最后一位当做基准,
- 看倒数第二位比倒数第一位大还是小,如果比倒数第一位大,呢就把倒数二位当做基准,直到找到比它小的一位数,我们将其记为 a.
- 然后用这位数去和它之后的数进行比较,找到比它大的数中最小的一位b.
- 然后交换a和b的位置
- 再将a之后的数字逆序排列,就是最终的结果.
我觉得自己已经解释的很清楚了,但是为了大家更好的去理解这个过程,下面请看一张动图,就能更加清晰的掌握这个过程了.(图是借鉴领扣的)
(三)LeetCode AC代码
1 public class Solution { 2 public void nextPermutation(int[] nums) { 3 int i = nums.length - 2; 4 while (i >= 0 && nums[i + 1] <= nums[i]) { 5 i--; 6 } 7 if (i >= 0) { 8 int j = nums.length - 1; 9 while (j >= 0 && nums[j] <= nums[i]) { 10 j--; 11 } 12 swap(nums, i, j); 13 } 14 reverse(nums, i + 1); 15 } 16 17 private void reverse(int[] nums, int start) { 18 int i = start, j = nums.length - 1; 19 while (i < j) { 20 swap(nums, i, j); 21 i++; 22 j--; 23 } 24 } 25 26 private void swap(int[] nums, int i, int j) { 27 int temp = nums[i]; 28 nums[i] = nums[j]; 29 nums[j] = temp; 30 } 31 }
虽然不是很难,但是自己还是搞了很久.
自己将其总结下来,相信慢慢的自己会有思路去写算法.