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

Posted on 2016-04-04 10:33  Iltxy  阅读(575)  评论(0)    收藏  举报

算法背景:

   昨天在做蓝桥杯的练习题目时,碰到一个题目!题目如下:

    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     }
View Code

 

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3