有多种方法可以解决这个问题,但恕我直言,最简单的方法是使用递归。下面我提供了 iterative 和 recursive 方法来解决生成集合的所有组合的问题。这两种方法的总体思路是生成属于您要选择的元素的索引集。
对于递归方法,您需要跟踪三项信息:一个布尔数组,指示是否选择了一个项目,您在项目列表中的位置以及一个变量 r,用于跟踪项目的数量剩余要选择的元素。然后只要 r != 0 (意味着你仍然需要选择 r 个元素来完成这个组合)你回溯选择一个你还没有选择的元素并在数组中前进。
下面显示的代码取自我的github repo。
/**
* Here we present two methods (recursive and iterative) of generating all
* the combinations of a set by choosing only r of n elements.
*
* Time Complexity: O( n choose r )
*
* @author William Fiset, Micah Stairs
**/
public class Combinations {
// This method finds all the combinations of size r in a set
public static void combinationsChooseR(int[] set, int r) {
if (r < 0) return;
if (set == null) return;
boolean [] used = new boolean[set.length];
combinations(set, r, 0, used);
}
// To find all the combinations of size r we need to recurse until we have
// selected r elements (aka r = 0), otherwise if r != 0 then we need to select
// an element which is found after the position of our last selected element
private static void combinations(int[] set, int r, int at, boolean[] used) {
final int N = set.length;
// If there are more elements left to select than what are available
// This is a short circuiting optimization we can take advantage of
int elementsLeftToPick = N - at;
if (elementsLeftToPick < r) return;
// We selected 'r' elements so we found a valid subset!
if (r == 0) {
System.out.print("{ ");
for (int i = 0; i < N; i++)
if (used[i])
System.out.print(set[i] + " ");
System.out.println("}");
} else {
for (int i = at; i < N; i++) {
// Try including this element
used[i] = true;
combinations(set, r - 1, i + 1, used);
// Backtrack and try the instance where we did not include this element
used[i] = false;
}
}
}
// Use this method in combination with a do while loop to generate all the
// combinations of a set choose r in a iterative fashion. This method returns
// false once the last combination has been generated.
// NOTE: Originally the selection needs to be initialized to {0,1,2,3 ... r-1}
public static boolean nextCombination(int[] selection, int N, int r) {
if (r > N) throw new IllegalArgumentException("r must be <= N");
int i = r - 1;
while (selection[i] == N - r + i)
if (--i < 0) return false;
selection[i]++;
for (int j = i + 1; j < r; j++) selection[j] = selection[i] + j - i;
return true;
}
public static void main(String[] args) {
// Recursive approach
int R = 3;
int[] set = {1,2,3,4,5};
combinationsChooseR(set, R);
// prints:
// { 1 2 3 }
// { 1 2 4 }
// { 1 2 5 }
// { 1 3 4 }
// { 1 3 5 }
// { 1 4 5 }
// { 2 3 4 }
// { 2 3 5 }
// { 2 4 5 }
// { 3 4 5 }
// Suppose we want to select all combinations of colors where R = 3
String[] colors = {"red", "purple", "green", "yellow", "blue", "pink"};
R = 3;
// Initialize the selection to be {0, 1, ... , R-1}
int[] selection = { 0, 1, 2 };
do {
// Print each combination
for (int index : selection)
System.out.print(colors[index] + " ");
System.out.println();
} while( nextCombination(selection, colors.length, R) );
// prints:
// red purple green
// red purple yellow
// red purple blue
// red purple pink
// red green yellow
// red green blue
// red green pink
// red yellow blue
// red yellow pink
// red blue pink
// purple green yellow
// purple green blue
// purple green pink
// purple yellow blue
// purple yellow pink
// purple blue pink
// green yellow blue
// green yellow pink
// green blue pink
// yellow blue pink
}
}