算法背景:
昨天在做蓝桥杯的练习题目时,碰到一个题目!题目如下:
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)
代码实现:
1 private static String nextPremium(int[] target) { 2 if (target == null || target.length == 0) { //如果为空,或者长度为0,则直接返回 3 return ""; 4 } 5 String result = ""; 6 if (target.length == 1) { 7 return target[0] +""; 8 } 9 10 // 查找右侧最长的递减序列 11 int index = target.length - 1; 12 while(index >0 && target[index - 1] >= target[index] ){ 13 index--; 14 } 15 if (index == 0) { 16 return "该序列组合已经是当前数字序列下最大的"; 17 } 18 // 右侧递减序列中最大值的左侧数字的索引 19 int maxLeft = index - 1; 20 // 查找右侧递减数列中的大于index值的最小值 21 int i = 0; 22 for(i = target.length - 1 ; i >= index ; i--){ 23 if (target[i] > target[maxLeft]) { 24 maxLeft = i; 25 break; 26 } 27 } 28 // 将右侧递减序列中的最大值左侧的一个元素与右侧大于index值得最小值互换 29 int tmp = target[maxLeft]; 30 target[maxLeft] = target[index - 1]; 31 target[index - 1] = tmp; 32 // 再将maxLeft右侧的数组倒转 33 int[] rightArr = new int[target.length - index]; 34 int j = 0; 35 for(i = target.length - 1 ; i >= index ;i-- , j++){ 36 rightArr[j] = target[i]; 37 } 38 j = 0; 39 40 for(i = index; i < target.length ; i++ ,j++){ 41 target[i] = rightArr[j]; 42 } 43 // 将新数组转化为字符串 44 for(i = 0 ; i < target.length ; i++){ 45 result += target[i]; 46 } 47 return result; 48 }
浙公网安备 33010602011771号