秒杀排列组合(上)————排列篇

来源:互联网 发布:美国非农数据最新信息 编辑:程序博客网 时间:2024/06/04 00:29

首先为什么要写排列组合?因为排列组合在数学中占有重要的地位,其与概率论也有密切关系;并且排列组合问题在求职的笔试,面试出现的概率特别高,而我在网上又没有搜到比较全面题型的文章;同时,我觉得编写排列组合程序对学习递归也是很有帮助的;当然,最重要的原因是排列组合本身就很有趣!所以就总结下排列组合的各种问法,分两篇写:上篇排列下篇组合


排列篇

组合篇地址http://blog.csdn.net/nash_/article/details/8315418


首先从各大IT公司的题中总结出排列组合的对象都是整形数组或字符数组,排列问题可以按输入数据分为两大类:输入数据有重复无重复,又可以按输出数据分为两大类:输出数据有重复重复;而排列问题也偶尔会考非递归。


首先提一下全排列的几种算法:

  1——字典序法
  2——递增进位数制法;
  3——递减进位数制法
  4——邻位交换法
  5——n进制数法
  6——递归生成法
  7——循环移位法
  8——回溯法

由本文的目的是总结排列的各种题型,而不是针对某个题型的各种算法,并且由于篇幅有限,感兴趣的朋友可以参考:

http://cache.baidu.com/cm=9f65cb4a8c8507ed4fece763104c8c711923d030678197027fa3c215cc790b1a0161e4bf233f405a8e90613c47f81641e1a43379360622e4cb998e4c8beb932e7f8a2633734ad74705d36ef58d197bd565cd1abfa00e96b0e741e3b9d3a3c82554dd22026df1f39c2c0203cb1fe76541f4d1985f655a07c9e7&p=8b2a9f0e96934eab5bacd3204a4c&user=baidu


由于侧重点在输入数据无重复,所以先看输入数据无重复类型


其中又可以分为全排列分组后排列

首先写基本的全排列


1.输出数组a的全排列(不可重复取)

如a={1,2,3}。输出123,132,213,231,312,321

这个是最基本,也是最经典的排列

算法思想:可以输出1加上23的全排列,2加13的全排列,3加上12的全排列,运用递归求比如23的全排列..依次递归下去;比如现在要2开头求全排,首先要交换1,2的位置,让a[0]变为2,在用递归求13的所有全排列,前面加个2就是2开头的所有全排列了,最后运用回溯再把1,2调换回来。

代码清单:

[java] view plaincopyprint?
  1. public class PaiLie {  
  2.       
  3.     public void runPermutation(int[] a){  
  4.         getAllPermutation(a, 0);  
  5.     }  
  6.     /*index用于控制如上述分析中2加上13的所有全列的*/  
  7.     public void getAllPermutation(int[] a,int index){  
  8.   
  9.         /*与a的元素个数相同则输出*/  
  10.         if(index == a.length-1){  
  11.             for(int i = 0; i < a.length; i++){  
  12.                 System.out.print(a[i] + " ");  
  13.             }  
  14.             System.out.println();  
  15.             return;  
  16.         }  
  17.           
  18.         for(int i = index; i < a.length; i++){  
  19.               
  20.             swap(a ,index, i);  
  21.             getAllPermutation(a, index+1);  
  22.             swap(a ,index, i);  
  23.         }  
  24.     }  
  25.     public void swap(int[] a, int i, int j) {  
  26.       
  27.         int temp = a[i];  
  28.         a[i] = a[j];  
  29.         a[j] = temp;  
  30.     }  
  31.   
  32.     public static void main(String[] args) {  
  33.           
  34.         PaiLie robot = new PaiLie();  
  35.         int[] a = {1,2,3};  
  36.         robot.runPermutation(a);  
  37.           
  38.     }  
  39. }  


2.输出数组a的全排列(可重复取)

如a={1,2}。输出11,12,21,22

如果知道a的length,可以用暴力法求解(n的循环)

如果不知道a的length的情况下:

算法思想:用一个辅助空间b数组存储待输出的排列,用一个参数index记录一个排列的个数

代码清单:

[java] view plaincopyprint?
  1. public class PaiLie {  
  2.       
  3.     public void runPermutation(int[] a) {  
  4.           
  5.         if(null == a || a.length == 0)  
  6.             return;  
  7.               
  8.         int[] b = new int[a.length];//辅助空间,保存待输出排列数  
  9.         getAllPermutation(a, b, 0);  
  10.     }  
  11.   
  12.     public void getAllPermutation(int[] a, int[] b, int index) {  
  13.           
  14.         if(index == a.length){  
  15.             for(int i = 0; i < index; i++){  
  16.                 System.out.print(b[i] + " ");  
  17.             }  
  18.             System.out.println();  
  19.             return;  
  20.         }  
  21.               
  22.         for(int i = 0; i < a.length; i++){  
  23.               
  24.             b[index] = a[i];  
  25.             getAllPermutation(a, b, index+1);  
  26.         }  
  27.           
  28.     }  
  29.       
  30.     public static void main(String[] args){  
  31.           
  32.         PaiLie robot = new PaiLie();  
  33.           
  34.         int[] a = {1,2,3};  
  35.         robot.runPermutation(a);  
  36.   
  37.     }  
  38.   
  39. }  

3.输出数组a的全排列(非递归)

如a={1,2,3}。输出123,132,213,231,312,321

全排列的非递归算法也不唯一,我写一个最常用的按字典序非递归算法

所谓字典序就是按照排列数的从大到小或从小到大输出,如123,132,2..3...

算法思想:如果能按顺序输出序列是这个算法的核心,为了保证按顺序输出先对数组a进行排序。然后从后向前找到第一个顺序对(12是顺序对,21不是)标记为i,然后再从后面向前找到第一个比i大的数,记录为j,随后交换i,j对应的值,再逆序数组a[i+1]到a[length-1]。听到这里大家一定很迷糊,我们来举个例子,比如说2431这个数我们先在i,因为31是逆序,43是逆序,24是顺序,所以i=0;接着我们找j,第一个比2大的数是3,所以j=3,然后交换i,j变成(3,4,2,1)我们看看为什么要交换2,3?因为这个算法的核心思想是按字典序,而2431是以2开头的最大排列,下一个数就得是以3开头了(如果原数是2341按算法就是先要变成2431),接着3421这个数要进行i+1到length-1之间的逆序,变成3124,这个是2431的下一个数。所以可以看出交换后的数从下位开始到最后一定是一个逆序排列,所以逆序后才变成了相对的“最小值”。

--代码清单:

[java] view plaincopyprint?
  1. import java.util.Arrays;  
  2.   
  3.   
  4. public class PaiLie {  
  5.       
  6.         public void runNoRecursionOfPermutation(int[] a){  
  7.           
  8.         Arrays.sort(a);//对数组排序   
  9.         while(true){  
  10.               
  11.             printArray(a);//输出一个排列  
  12.               
  13.             int i;//从后向前,记录一对顺序值中的小值下标  
  14.             int j;//从后向前,记录比i大的第一个数  
  15.               
  16.             for(i = a.length-2; i >= 0; i--){  
  17.                   
  18.                 if(a[i] < a[i+1])//如果找到i跳出  
  19.                     break;  
  20.                 else if(i == 0)//说明是最大逆序数退出函数  
  21.                     return;  
  22.             }  
  23.             for(j = a.length-1; j > i; j--){  
  24.                 if(a[j] > a[i])//找到j跳出  
  25.                     break;  
  26.             }  
  27.             swap(a, i, j);//交换i,j  
  28.             reverse(a, i+1, a.length-1);//翻转  
  29.         }  
  30.     }  
  31.     public void swap(int[] a, int i, int j) {  
  32.       
  33.         int temp = a[i];  
  34.         a[i] = a[j];  
  35.         a[j] = temp;  
  36.     }  
  37.     public void reverse(int[] a, int i, int j){  
  38.         while(i < j)  
  39.             swap(a, i++, j--);  
  40.     }  
  41.       
  42.         public void printArray(int[] a) {  
  43.           
  44.         for(int i = 0; i < a.length; i++){  
  45.             System.out.print(a[i] + " ");  
  46.         }  
  47.         System.out.println();  
  48.     }  
  49.     public static void main(String[] args) {  
  50.           
  51.         PaiLie robot = new PaiLie();  
  52.         int[] a = {1,2,3};  
  53.         robot.runNoRecursionOfPermutation(a);  
  54.           
  55.     }  
  56. }  


4.输出从数组a中取n个数的所有排列

如a={1,2,3} n=2输出12,21,13,31,23,32

这道题可以看作是排列组合的综合题,有关组合问题也可以参考我写的组合篇

http://blog.csdn.net/nash_/article/details/8315418

算法思想:求出a中选取n个数的所有组合,分别对其进行全排列。

代码清单:

[java] view plaincopyprint?
  1. public class PaiLie {  
  2.       
  3.     public void runPermutation(int[] a, int n) {  
  4.           
  5.         if(null == a || a.length == 0 || n <= 0 || n > a.length)  
  6.             return;  
  7.               
  8.         int[] b = new int[n];//辅助空间,保存待输出组合数  
  9.         getCombination(a, n , 0, b, 0);  
  10.     }  
  11.   
  12.     public void getCombination(int[] a, int n, int begin, int[] b, int index) {  
  13.           
  14.         if(n == 0){//如果够n个数了,输出b数组  
  15.               
  16.             getAllPermutation(b,0);//得到b的全排列  
  17.             return;  
  18.         }  
  19.               
  20.         for(int i = begin; i < a.length; i++){  
  21.               
  22.             b[index] = a[i];  
  23.             getCombination(a, n-1, i+1, b, index+1);  
  24.         }  
  25.           
  26.     }  
  27.     public void getAllPermutation(int[] a,int index){  
  28.   
  29.         /*与a的元素个数相同则输出*/  
  30.         if(index == a.length-1){  
  31.             for(int i = 0; i < a.length; i++){  
  32.                 System.out.print(a[i] + " ");  
  33.             }  
  34.             System.out.println();  
  35.             return;  
  36.         }  
  37.           
  38.         for(int i = index; i < a.length; i++){  
  39.               
  40.             swap(a ,index, i);  
  41.             getAllPermutation(a, index+1);  
  42.             swap(a ,index, i);  
  43.         }  
  44.     }  
  45.     public void swap(int[] a, int i, int j) {  
  46.       
  47.         int temp = a[i];  
  48.         a[i] = a[j];  
  49.         a[j] = temp;  
  50.     }  
  51.     public static void main(String[] args){  
  52.           
  53.         PaiLie robot = new PaiLie();  
  54.           
  55.         int[] a = {1,2,3};  
  56.         int n = 2;  
  57.         robot.runPermutation(a,n);  
  58.   
  59.     }  
  60.   
  61. }  


输入数据有重复类型:

这类如a={1,3,2,3} 3出现了两次,用以上排列会造成输出重复。

5.输出数组a的全排列(递归)

如a={1,1,2}输出112,121,211

算法思想:我们改进一下1的算法,在for中判断是否有包含重复元素,也就是index和i之间是否有和a[i]相等的值,比如对于2313这个数列,当index=0(a[index] = 2),i=3(a[i] = 3)的时候,如果要交换这两个数变成3312的话就是计算重复了,因为它们之间有1个3,当i=1的时候,它已经转换过3312了。所以加一个函数判断中间有没有包含重复元素,如有没有重复元素,再做交换。

代码清单:

[java] view plaincopyprint?
  1. public class PaiLie {  
  2.       
  3.     public void runPermutation(int[] a){  
  4.         getAllPermutation(a, 0);  
  5.     }  
  6.     /*index用于控制如上述分析中2加上13的所有全列的*/  
  7.     public void getAllPermutation(int[] a,int index){  
  8.   
  9.         /*与a的元素个数相同则输出*/  
  10.         if(index == a.length-1){  
  11.             for(int i = 0; i < a.length; i++){  
  12.                 System.out.print(a[i] + " ");  
  13.             }  
  14.             System.out.println();  
  15.             return;  
  16.         }  
  17.           
  18.         for(int i = index; i < a.length; i++){  
  19.               
  20.             if(contains(a, index ,i))  
  21.                 continue;  
  22.             swap(a ,index, i);  
  23.             getAllPermutation(a, index+1);  
  24.             swap(a ,index, i);  
  25.         }  
  26.     }  
  27.       
  28.     public boolean contains(int[] a, int m, int n) {  
  29.           
  30.          for (int i = m; i < n; i++) {  
  31.   
  32.                if (a[i] == a[n])  
  33.                 return true;  
  34.               }  
  35.         return false;  
  36.     }  
  37.     public void swap(int[] a, int i, int j) {  
  38.       
  39.         int temp = a[i];  
  40.         a[i] = a[j];  
  41.         a[j] = temp;  
  42.     }  
  43.   
  44.     public static void main(String[] args) {  
  45.           
  46.         PaiLie robot = new PaiLie();  
  47.         int[] a = {1,1,2};  
  48.         robot.runPermutation(a);  
  49.           
  50.     }  
  51. }  


6.输出数组a的全排列(非递归)

如a={1,1,2}输出112,121,211

算法思想:对于重复输入数据的非递归,我们仍然可以用算法3,因为排序后,相等的数都挨着,而我们认为相等的两个数不构成“有序”,所以就不会造成重复,并且按字典序输出所有排列。

代码清单:

[java] view plaincopyprint?
  1. import java.util.Arrays;  
  2.   
  3.   
  4. public class PaiLie {  
  5.       
  6.        public void runNoRecursionOfPermutation(int[] a){  
  7.           
  8.         Arrays.sort(a);//对数组排序   
  9.         while(true){  
  10.               
  11.             printArray(a);//输出一个排列  
  12.               
  13.             int i;//从后向前,记录一对顺序值中的小值下标  
  14.             int j;//从后向前,记录比i大的第一个数  
  15.               
  16.             for(i = a.length-2; i >= 0; i--){  
  17.                   
  18.                 if(a[i] < a[i+1])//如果找到i跳出  
  19.                     break;  
  20.                 else if(i == 0)//说明是最大逆序数退出函数  
  21.                     return;  
  22.             }  
  23.             for(j = a.length-1; j > i; j--){  
  24.                 if(a[j] > a[i])//找到j跳出  
  25.                     break;  
  26.             }  
  27.             swap(a, i, j);//交换i,j  
  28.             reverse(a, i+1, a.length-1);//翻转  
  29.         }  
  30.     }  
  31.     public void swap(int[] a, int i, int j) {  
  32.       
  33.         int temp = a[i];  
  34.         a[i] = a[j];  
  35.         a[j] = temp;  
  36.     }  
  37.     public void reverse(int[] a, int i, int j){  
  38.         while(i < j)  
  39.             swap(a, i++, j--);  
  40.     }  
  41.     public void printArray(int[] a) {  
  42.           
  43.         for(int i = 0; i < a.length; i++){  
  44.             System.out.print(a[i] + " ");  
  45.         }  
  46.         System.out.println();  
  47.     }  
  48.     public static void main(String[] args) {  
  49.           
  50.         PaiLie robot = new PaiLie();  
  51.         int[] a = {1,2,3};  
  52.         robot.runNoRecursionOfPermutation(a);  
  53.           
  54.     }  
  55. }  


如果您有其他的排列问题请告诉博主,谢谢!


==================================================================================================

  作者:nash_  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址http://blog.csdn.net/nash_/article/details/8351611

===================================================================================================

0 0
原创粉丝点击