代码改变世界

Permutation&Combination递归实现

2012-07-28 21:20  coodoing  阅读(812)  评论(0)    收藏  举报

 

(1)关于字符串排列的问题

输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

解题思路:固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。这样写成递归程序如下:

   1: public void doPermutation(char[] arr, int start, int end) {
   2:         if (start == end) {
   3:             for (int i = 0; i <= end; i++) {
   4:                 System.out.print(arr[i]);
   5:             }
   6:             System.out.println();
   7:         } else {
   8:             for (int i = start; i <= end; i++) {//(让指针start分别指向每一个数)   
   9:                 char temp = arr[start];//交换数组第一个元素与后续的元素    
  10:                 arr[start] = arr[i];
  11:                 arr[i] = temp;
  12:  
  13:                 doPermutation(arr, start + 1, end);//后续元素递归全排列    
  14:  
  15:                 temp = arr[start];//将交换后的数组还原    
  16:                 arr[start] = arr[i];
  17:                 arr[i] = temp;
  18:             }
  19:         }
  20:     }
  21:  
  22:     public void doPermutation2(char[] arr, int start, int len) {
  23:         if (start == len - 1) {
  24:             for (int i = 0; i < len; i++) {
  25:                 System.out.print(arr[i]);
  26:             }
  27:             System.out.println();
  28:         } else {
  29:             for (int i = start; i < len; i++) {
  30:                 swap(arr, start, i);
  31:                 doPermutation2(arr, start + 1, len);
  32:                 swap(arr, start, i);
  33:             }
  34:         }
  35:     }
  36:  
  37:     public void swap(char[] arr, int i, int j) {
  38:         char temp;
  39:         temp = arr[i];
  40:         arr[i] = arr[j];
  41:         arr[j] = temp;
  42:     }

        如果字符串中有重复字符的话,上面的那个方法肯定不会符合要求的,因此现在要想办法来去掉重复的数列。

去掉重复的全排列的递归实现
由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。
换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。
这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。下面给出完整代码:

   1: boolean isSwap(int begin, int end)  
   2:     {  
   3:         int i ;  
   4:         for(i = begin ; i <end ; i++)  
   5:         {  
   6:             if(i == end)  
   7:                 return false;  
   8:         }  
   9:         return true;  
  10:     }  
  11:     
  12:     public void removeDulPermutation(char[] arr, int start, int len) {
  13:         if (start == len - 1) {
  14:             for (int i = 0; i < len; i++) {
  15:                 System.out.print(arr[i]);
  16:             }
  17:             System.out.println();
  18:         } else {
  19:             for (int i = start; i < len; i++) {
  20:                 if(isSwap(start,i))
  21:                 {
  22:                     swap(arr, start, i);
  23:                     removeDulPermutation(arr, start + 1, len);
  24:                     swap(arr, start, i);
  25:                 }
  26:             }
  27:         }
  28:     }

 

(2)关于组合的问题输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。假设我们想在长度为n的字符串中求m个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。这两种选择都很容易用递归实现。

   1: class Combination {
   2:     char[] charArr;
   3:  
   4:     public Combination(char[] arr) {
   5:         charArr = arr;
   6:     }
   7:  
   8:     public void doCombination(char[] arr) {
   9:         if (arr.length == 0)
  10:             return;
  11:  
  12:         Stack<Character> stack = new Stack<Character>();
  13:         ArrayList<Character> list=new ArrayList<Character>();  
  14:         
  15:         for (int i = 1; i <= arr.length; i++) {
  16:             combination(arr, 0, i, stack);            
  17:         }
  18:         System.out.println("字符串组合2");
  19:         for (int i = 1; i <= arr.length; i++) {
  20:             combination2(arr, 0, i, list);        
  21:         }
  22:     }
  23:  
  24:     public void combination(char[] arr, int begin, int number,
  25:             Stack<Character> stack) {
  26:         if (number == 0) {
  27:             System.out.println(stack.toString());
  28:             return;
  29:         }
  30:         if (begin == arr.length) {
  31:             return;
  32:         }
  33:         stack.push(arr[begin]);
  34:         combination(arr, begin + 1, number - 1, stack);
  35:         stack.pop();
  36:         combination(arr, begin + 1, number, stack);
  37:     }
  38:     
  39:     public void combination2(char[] arr, int begin, int number,
  40:             ArrayList<Character> list) {
  41:          if(number==0){  
  42:                 System.out.println(list.toString());  
  43:                 return ;  
  44:             }  
  45:             if(begin==arr.length){  
  46:                 return;  
  47:             }  
  48:             list.add(arr[begin]);  
  49:             combination2(arr,begin+1,number-1,list);  
  50:             list.remove((Character)arr[begin]);  
  51:             combination2(arr,begin+1,number,list);  
  52:     }
  53: }

 最后附一个排列组合题:用1、2、2、3、4、5这六个数字,打印出所有不同的排列,如:512234、412345等,要求:"4"不能在第三位, "3"与"5"不能相连。 

主要参考资料如下:

http://zhedahht.blog.163.com/blog/static/2541117420114172812217/

http://blog.csdn.net/hackbuteer1/article/details/7462447

http://shukuiyan.iteye.com/blog/1095637#