算法学习笔记(三)——全排列生成算法:next_permutation

算法背景:
昨天在做蓝桥杯的练习题目时,碰到一个题目!题目如下:
  A A 2 2 3 3 4 4, 一共4对扑克牌。请你把它们排成一行。要求:两个A中间有1张牌,两个2之间有2张牌,两个3之间有3张牌,两个4之间有4张牌。请填写出所有符合要求的排列中,字典序最小的那个。
 这里有必要说一下字典序:默认情况下,比较两个序列的字典序是挨个比较两个序列的字符,如果相同则比较下一字符,直到出现两字符不一样。其ASCII码较大者,即为字典序大者!因为A的ASCII的值肯定是最大的!则可将A换成任意字符(比4大就行),这里以5举例说明。则此题关键是求出一个数值最小且满足位置要求的序列!显然,数值最小的序列为:1122334455,数值最大的序列:5544332211,虽然这两个序列都不符合要求,但是却可以提供一个范围!即符合要求的序列数值肯定位于(1122334455 , 5544332211)!这时就只需要运用全排列生成算法,找到比1122334455大的所有序列中的最小值,即为所求!那么问题来了:这个数怎么求呢?
问题:
给定任意一个序列,生成下一个较大的序列
数学推演:
我们用<a1,a2,a3,a4,······an>表示一个序列A!若从an开始逆向查找,一定可以找到一个递增序列(当然,该数列的长度可能为1)。不妨设该序列的最大值为am,设a(m- 1)表示am左侧最近的元素。则递增序列可表示为<am ......an>,则有以下结论:
1、如果固定am左侧数列,只允许变化递增数列的排列方式,则该数列一定是该条件下数值最大的序列
2、a(m-1) < am
由结论1可知,如果在递增数列中找到元素min(a(j) > a(m-1))(j从n开始逆向取值,依次取值n - 1,n-2....直到m),并且将a(j )和a(m-1)的位置互换,设该序列为B,则B>A(数值)。如果调整<a(m),a(m+1)......a(j)......a(n)>序列的位置至最小序列(直接反转即可得到最小序列),则得到的序列C,满足不存在任何序列D,满足C>D>A(数值),故序列C即为所求;
        举个例子:求<3  6 4  2>的下一较大序列!按照上述步骤,递增序列为:6  4   2,在递增序列中比3大的最小值是4,那么将3和4互换位置之后的序列为<4  6  3  2>,最后将6  3  2按字典序最小排序,即可得到最终结果:< 4 2 3 6>
复杂度:
最好的情况当然是,递增序列的长度为1,只需要将倒数第二个和最后一个元素互换位置得到的序列即为所求!时间复杂度为O(2)
    最坏的情况是,递增序列的长度为n-1,需要将长度为n-1的序列全部倒转,时间复杂度为O(n)
 故,该算法的时间复杂度为O(n)
代码实现:
话不多说,直接上代码:

private static String nextPremium(int[] target) { if (target == null || target.length == 0) { //如果为空,或者长度为0,则直接返回 return ""; } String result = ""; if (target.length == 1) { return target[0] +""; } // 查找右侧最长的递减序列 int index = target.length - 1; while(index >0 && target[index - 1] >= target[index] ){ index--; } if (index == 0) { return "该序列组合已经是当前数字序列下最大的"; } // 右侧递减序列中最大值的左侧数字的索引 int maxLeft = index - 1; // 查找右侧递减数列中的大于index值的最小值 int i = 0; for(i = target.length - 1 ; i >= index ; i--){ if (target[i] > target[maxLeft]) { maxLeft = i; break; } } // 将右侧递减序列中的最大值左侧的一个元素与右侧大于index值得最小值互换 int tmp = target[maxLeft]; target[maxLeft] = target[index - 1]; target[index - 1] = tmp; // 再将maxLeft右侧的数组倒转 int[] rightArr = new int[target.length - index]; int j = 0; for(i = target.length - 1 ; i >= index ;i-- , j++){ rightArr[j] = target[i]; } j = 0; for(i = index; i < target.length ; i++ ,j++){ target[i] = rightArr[j]; } // 将新数组转化为字符串 for(i = 0 ; i < target.length ; i++){ result += target[i]; } return result; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值