背景很簡單,就是從給定的m個不同的元素中選出n個,輸出所有的組合情況!
例如:從1到m的自然數中,選擇n(n<=m)個數,有多少種選擇的組合,將其輸出!
本方案的代碼實現邏輯是比較成熟的方案:
* 一個bit位(boolean)一維數組中,初始化全為0(false), 然后給左邊的n個位初始化為1(true)。 * <1> 從左向右找第一個10的位置,將10換位程01,然后將這個01左邊的所有的1全都移位到數組的最左邊,此時得到的1所在位置下標對應序列即為一個組合數。 * <2> 循環重復上面的<1>步驟的操作,直到所有的1都移動到最右邊為止。
先不多說其他的,直接將代碼貼在這里,以供有需要的伙伴借鑒:
1 /** 2 * @author "shihuc" 3 * @date 2016年12月1日 4 */ 5 6 7 import java.util.ArrayList; 8 import java.util.Arrays; 9 10 /** 11 * @author chengsh05 12 * 13 * 組合算法實現,支持產品列表頁的篩選模塊實現全靜態化。 14 * 15 * 給定m個不同的數,從中選擇出n個數的不同選擇方法有多少種? 16 * 答案:一共有 n!/(n-m)!*m! 17 * 18 * 存儲計算出來的組合結構,組合元素之間用逗號分隔 19 * 例如1,2,3的全組合: 20 * "1,", "2,", "3,","1,2,", "1,3,", "2,3,", "1,2,3," 21 */ 22 public class Combination { 23 24 /** 25 * 從m個元素中取出n個元素,獲取排序好了的組合數列表,同一個組合中的元素按從小到大排序。 26 * 27 * @param m 組合的元素基數 28 * @param n 組合的被選元素個數 29 * @return 排序好后的組合列表 30 * @throws Exception 31 */ 32 public ArrayList<String> getCombinations(int m, int n) throws Exception{ 33 34 if(m < n){ 35 throw new IllegalCombinationInputException(); 36 } 37 38 ArrayList<String> resCom = calcCombination(m, n); 39 40 return resCom; 41 } 42 43 /** 44 * 通過移位方式,計算給定m個數中取出n個數的組合列表。 45 * 46 * 具體思路: 47 * 一個bit位(boolean)數組中,初始化全為0(false), 然后給左邊的n個位初始化為1(true)。 48 * <1> 從左向右找第一個10的位置,將10換位程01,然后將這個01左邊的所有的1全都 移位到數組的最左邊,此時得到的1所在位置下標對應的序列即為一個組合數。 49 * <2> 循環重復上面的<1>步驟的操作,直到所有的1都移動到最右邊為止。 50 * 51 * @param m 輸入的基數個數 52 * @param n 組合被選元素格式 53 * @return 原始組合數列表,即沒有排序的組合 54 */ 55 private ArrayList<String> calcCombination(int m, int n){ 56 boolean base[] = new boolean[m]; 57 Arrays.fill(base, false); 58 for(int i=0; i<n; i++){ 59 base[i] = true; 60 } 61 return combination(base,m,n); 62 } 63 64 private ArrayList<String> combination(boolean a[], int m, int n){ 65 ArrayList<String> combination = new ArrayList<String>(); 66 while(!isAllZeroLeft(a, m, n)){ 67 for(int i=0; i<m-1; i++){ 68 if(a[i] == true && a[i+1] == false){ 69 String elem = calcElement(a); //計算出一個組合元素 70 //System.out.println(elem); 71 combination.add(elem); 72 oneZeroSwap(a, i, i+1); //完成10/01換位 73 moveFrontOneToLeft(a, i); //完成剩余左邊的1全向最左邊搬移操作 74 break; 75 } 76 } 77 } 78 79 //最后一個元素也是組合的一個,即所有的1(true)都到了數組的最右邊 80 combination.add(calcElement(a)); 81 return combination; 82 } 83 84 /** 85 * 異或操作實現不開辟新的存儲空間完成兩個數的位置交換。 86 * 87 * @param a 待操作數所在的數組 88 * @param x 待交換位置的第一個數在數組中的下標 89 * @param y 待交換位置的第二個數在數組中的下標 90 */ 91 private void oneZeroSwap(boolean a[], int x, int y){ 92 a[x] = a[x] ^ a[y]; 93 a[y] = a[y] ^ a[x]; 94 a[x] = a[x] ^ a[y]; 95 } 96 97 /** 98 * 判斷10作01交換位置后,是否實現了數組a中右端的n個元素全為1(true),這個結果作為10/01換位結束標識 99 * 100 * @param a 10/01換位的輸入數組 101 * @param m 計算組合的元數據個數 102 * @param n 計算組合的被選取元素個數 103 * @return true表示10/01換位結束,false表示還可以繼續 104 */ 105 private boolean isAllZeroLeft(boolean a[], int m, int n){ 106 int gap = m - n; 107 for(int i=0; i<gap; i++){ 108 if(a[i]){ 109 return false; 110 } 111 } 112 return true; 113 } 114 115 /** 116 * 將10/01換位之后數組左邊的全部1都搬移到數組的最左邊。 117 * 118 * @param a 待操作的組合數組 119 * @param end 指明搬移操作的范圍,在end數組下標左邊的進行搬移, 這個end的值小於數組的長度 120 */ 121 private void moveFrontOneToLeft(boolean a[], int end){ 122 int oneCnt = 0; 123 for(int i=0; i<end; i++){ 124 if(a[i]){ 125 oneCnt++; 126 a[i] = false; 127 } 128 } 129 for(int i=0; i<oneCnt; i++){ 130 a[i] = true; 131 } 132 } 133 134 /** 135 * 計算當前數組中的組合元素的值,數組元素為1(true)的對應的下標的全集,即為所需的一個組合元素值 136 * 137 * @param a 待操作的組合數組 138 * @return 一個組合的值, 去掉了最后的一個逗號分隔符 139 */ 140 private String calcElement(boolean a[]){ 141 String elem = ""; 142 for(int i=0; i<a.length; i++){ 143 if(a[i]){ 144 elem += (i+1) + ","; 145 } 146 } 147 return elem.substring(0, elem.length() - 1); 148 } 149 150 151 /** 152 * @param args 153 * @throws Exception 154 */ 155 public static void main(String[] args) throws Exception { 156 int m = 5, n = 2; 157 Combination combination = new Combination(); 158 ArrayList<String> coms = combination.getCombinations(m, n); 159 for(int i = 0; i<coms.size(); i++){ 160 System.out.println(coms.get(i)); 161 } 162 } 163 }
代碼中定義的Exception的類:
1 /** 2 * @author "shihuc" 3 * @date 2016年12月1日 4 */ 5 6 /** 7 * @author chengsh05 8 * 9 */ 10 public class IllegalCombinationInputException extends Exception{ 11 12 private static final long serialVersionUID = 678024281707796100L; 13 14 public IllegalCombinationInputException(){ 15 super("The combination base number should be not less than the selection number."); 16 } 17 18 }
此算法思路,在很多場景下還是值得借鑒的!
算法是計算機科學的靈魂,堅持算法學習和應用......