全組合 與全排列
阿新 • • 發佈:2019-01-02
一、全組合
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.全排列的非遞迴就是由後向前找替換數和替換點,然後由後向前找第一個比替換數大的數與替換數交換,最後顛倒替換點後的所有資料。