[LeetCode]Next Permutation

题目描述


Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place, do not allocate extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

给出数组的的下一个排列。

解题思路


     对一个给定数据进行全排列,在各种场合经常会用到。组合数学中,生成全排列的方法有很多,卢开澄老师的《组合数学》中就介绍了三种:序数法,字典序法,临位互换法等。其中以字典序法由于算法简单,并且使用的时候可以依照当前状态获取下一个状态,直到所有排列全部完成,方便在程序中随要随用,应用比较广泛,STL中的Next_permutation也是使用此法。

    字典序,顾名思义就是按照字典的顺序(a-z, 1-9)。以字典序为基础,我们可以得出任意两个数字串的大小。比如 "1" < "12"<"13"。 就是按每个数字位逐个比较的结果。对于一个数字串,“123456789”, 可以知道最小的串是 从小到大的有序串“123456789”,而最大的串是从大到小的有序串“*987654321”。这样对于“123456789”的所有排列,将他们排序,即可以得到按照字典序排序的所有排列的有序集合。
如此,当我们知道当前的排列时,要获取下一个排列时,就可以范围有序集合中的下一个数(恰好比他大的)。比如,当前的排列时“123456879”, 那么恰好比他大的下一个排列就是“123456897”。 当当前的排列时最大的时候,说明所有的排列都找完了。

计算下一个排列的算法:

设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)对换pi,pk
  4)再将pj+1......pk-1pkpk+1......pn倒转得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个排列。

算法的证明这里就不再赘述了。

代码


LeetCode的测试用例中3,2,1的nextPermutation是1,2,3

public static void nextPermutation(int[] num) {
		int leftIndex;// 左值索引
		int rightIndex;// 右值索引
		int temp;
		int i, j;

		i = num.length - 2;
		//确定左值索引
		while (i >= 0 && num[i] >= num[i + 1]) {
			i--;
		}

		if (i < 0) {
			//根据题意,当已经是最大的数的时候,它的下一个全排列是其逆转
			i = 0;
			j = num.length - 1;
			while (i < j) {
				temp = num[i];
				num[i] = num[j];
				num[j] = temp;
				i++;
				j--;
			}
			return;
		}
		leftIndex = i;

		i = num.length - 1;
		//找到左值索引右边比左值大的数的最小值的索引值
		while (num[i] <= num[leftIndex]) {
			i--;
		}
		rightIndex = i;
		
		//交换左右值
		temp = num[leftIndex];
		num[leftIndex] = num[rightIndex];
		num[rightIndex] = temp;

		//逆转左值的右边序列
		i = leftIndex + 1;
		j = num.length - 1;
		while (i < j) {
			temp = num[i];
			num[i] = num[j];
			num[j] = temp;
			i++;
			j--;
		}
	}


 
 
 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值