1. 程式人生 > >全組合 與全排列

全組合 與全排列

一、全組合

複製程式碼
public static  void Combination( ) {
        /*基本思路:求全組合,則假設原有元素n個,則最終組合結果是2^n個。原因是:
         * 用位操作方法:假設元素原本有:a,b,c三個,則1表示取該元素,0表示不取。故去a則是001,取ab則是011.
         * 所以一共三位,每個位上有兩個選擇0,1.所以是2^n個結果。
         * 這些結果的點陣圖值都是0,1,2....2^n。所以可以類似全真表一樣,從值0到值2^n依次輸出結果:即:
         * 000,001,010,011,100,101,110,111 。對應輸出組合結果為:
        空,a, b ,ab,c,ac,bc,abc.
        這個輸出順序剛好跟數字0~2^n結果遞增順序一樣
        取法的二進位制數其實就是從0到2^n-1的十進位制數
         * ******************************************************************
         * * 
         * 
*/ String[] str = {"a" , "b" ,"c"}; int n = str.length; //元素個數。 //求出點陣圖全組合的結果個數:2^n int nbit = 1<<n; // “<<” 表示 左移:各二進位全部左移若干位,高位丟棄,低位補0。:即求出2^n=2Bit。 System.out.println("全組合結果個數為:"+nbit);
for(int i=0 ;i<nbit ; i++) { //結果有nbit個。輸出結果從數字小到大輸出:即輸出0,1,2,3,....2^n。 System.out.print("組合數值 "+i + " 對應編碼為: "); for(int j=0; j<n ; j++) { //每個數二進位制最多可以左移n次,即遍歷完所有可能的變化新二進位制數值了 int tmp = 1<<j ;
if((tmp & i)!=0) { //& 表示與。兩個位都為1時,結果才為1 System.out.print(str[j]); } } System.out.println(); } }
複製程式碼

執行流程:
舉例:3個元素:a,b,c。所以有2^3=8個組合結果:所以i=0,1,2,....7.對應應輸出 a,b,ab,c...abc  (注意a表示001,不是100.)
將i變成2進位制: i=1 = 001    i=2 =010 i=3=011
    (1)j=0 (1)j=0 (1)j=0    移1位: 1<<j == 001 1<<j == 001 1<<j == 001    和i=001相與,兩個位都為1,返回1 與i無相同位 和i=001相與,兩個位都為1,返回1   輸出:a 輸出a (2) j=1 (2) j=1 (2) j=1 再移一位: 1<<j ==010 1<<j ==010    1<<j ==010 與i=001相與。無相同1 和i相與,兩個位都為1,返回1 和i相與,兩個位都為1,返回1 輸出b 輸出b (3) j=2 3) j=2 (3) j=2 移一位 1<<j ==100 1<<j ==100 與i無相同位 與i無相同位 與i無相同位 所以i=001, 只輸出a. 所以i=010, 只輸出b. 所以011,輸出ab

************************************* * 可見上面每一個數字i,只會判斷判斷3次,因為只需要移三次位,二進位制就遍歷完了
* *************************************    

---------------------------------------------------------------------------------------------------------
另一個大同小異版本程式碼:
複製程式碼
        public static void combination1() {
            /*全組合:
             * 思路是利用二進位制的特性,每次加1即可遍歷所有位的不同情況,很好理解
            程式碼同上
                */
            String arr[] = { "a", "b", "c"};
            int all = arr.length;
            int nbit = 1 << all;
            for (int i = 0; i < nbit; i++) {
                StringBuffer sb = new StringBuffer();
                for (int j = 0; j < all; j++) {
                    if ((i & (1 << j)) != 0) {
                        sb.append(arr[j]);
                    }
                }
                System.out.println(sb);
            }
複製程式碼

二、全排列

     遞迴:

* 從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,如此遞迴處理,
* 從而得到所有元素的全排列。以對字串abc進行全排列為例,我們可以這麼做:以abc為例:
* 固定a,求後面bc的排列:abc,acb,求好後,a和b交換,得到bac
* 固定b,求後面ac的排列:bac,bca,求好後,c放到第一位置,得到cba
* 固定c,求後面ba的排列:cba,cab。

* 即遞迴樹:
     str:   a      b        c
        ab ac    ba bc      ca cb
  result:  abc acb    bac bca        cab cba

複製程式碼
public static void permutation1(String str ,String result ,int len){
        /* 全排列 遞迴實現 
      遞迴樹:
          str:          a            b                c
                      ab ac         ba   bc         ca   cb
        result:        abc acb        bac    bca      cab   cba
         */
        
          //結果 開始傳入""   空字元進入   len   是這個數的長度
          if(result.length()==len){            //表示遍歷完了一個全排列結果
           System.out.println(result);
          }
          else{
              for(int i=0;i<str.length();i++){
                  if(result.indexOf(str.charAt(i))<0){    //返回指定字元在此字串中第一次出現處的索引。
                      //System.out.println("字母:"+str.charAt(i));
                      permutation1(str, result+str.charAt(i), len);
                  }
              } 
          }
    }

public static void main(String args[]) throws Exception { 
         String s = "abc";
         String result = "";
         permutation1(s, result, s.length());
    } 
複製程式碼

  遞迴另一種寫法:或者採用July的方法:

 從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,如此遞迴處理,
* 從而得到所有元素的全排列。以對字串abc進行全排列為例,我們可以這麼做:以abc為例:
* 固定a,求後面bc的排列:abc,acb,求好後,a和b交換,得到bac
* 固定b,求後面ac的排列:bac,bca,求好後,c放到第一位置,得到cba
* 固定c,求後面ba的排列:cba,cab。

複製程式碼
public static void permutation(String[] str , int first,int end) {
        //輸出str[first..end]的所有排列方式
        if(first == end) {    //輸出一個排列方式
            for(int j=0; j<= end ;j++) {
                System.out.print(str[j]);
            }
            System.out.println();
        }
            
        for(int i = first; i <= end ; i++) {
            swap(str, i, first);
            permutation(str, first+1, end);  //固定好當前一位,繼續排列後面的
            swap(str, i, first);
        }    
    }
    
    private static void swap(String[] str, int i, int first) {
         String tmp;
         tmp = str[first];
         str[first] = str[i];
         str[i] = tmp;      
    }
· 
public static void main(String args[]) throws Exception { String[] str = {"a","b","c"}; permutation(str, 0, 2);    //輸出str[0..2]的所有排列方式
} }
複製程式碼

參考http://blog.csdn.net/morewindows/article/details/7370155 總結:

1.全排列就是從第一個數字起每個數分別與它後面的數字交換。

2.去重的全排列就是從第一個數字起每個數 分別與它後面非重複出現的數字交換。

3.全排列的非遞迴就是由後向前找替換數替換點,然後由後向前找第一個比替換數大的數與替換數交換,最後顛倒替換點後的所有資料。