组合问题
组合和排列有区别,排列是和顺序有关的,组合和顺序无关。例如:ABC和ACB,如果是组合的话,只能算一种情况。而对于排列而言,是两种情况。
练习题一:无重复的组合问题。
ABCDEF共5个元素,求任取3个的组合,分别有哪些结果。
完整java代码:
import java.util.List;
import java.util.Vector;
/**
* 组合枚举的递归方法(适用于不重复的字符串)
* ABCDE五个字母中取出三个 有多少种组合
*/
public class Test10 {
public static void main(String[] args) {
List<String> list = f("ABCDE", 3);
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i));
// }
for (String s:list ) {
System.out.println(s);
}
}
private static List f(String str, int k) {
List<String> list = new Vector();
if (k==0){
list.add("");
return list;
}
// 遍历串中的每一个字母
for (int i = 0; i < str.length(); i++) {
// 取一个字母
char x = str.charAt(i);
// 递归, 从现有的元素中,取k-1个。只取后面的,可以保证不重复。
List<String> result = f(str.substring(i+1), k-1);
// 将集合中有的元素,和现在取得元素拼接起来。
for (int j = 0; j < result.size(); j++) {
list.add(""+x+result.get(j));
}
}
return list;
}
}
练习题二:有重复的组合问题。
AABBBC共5个元素,求任取3个的组合,分别有哪些结果。
/**
* AABBBC 字母中取出三个 有多少种组合
*/
public class Test11 {
public static void main(String[] args) {
int[] data = {2,3,1};// 每个元素出现的次数
int[] x = new int[data.length]; // 每个元素实际取了几个,取法
f(data,x,0,3);
}
/**
*
* @param data 元素,不动。
* @param x 每个位置元素的取法
* @param k 当前考虑的位置
* @param target 距离目标的剩余名额
*/
public static void f(int[] data, int[] x, int k, int target) {
if (k==x.length){
if (target==0){
show(x);
}
return;
}
// k可以不取,最多(本题A的数量是2,而需要取3个,故A最多只能取2个)
for (int i = 0; i <= Math.min(target, data[k]); i++) {
x[k] = i;
f(data, x,k+1,target-i);
}
x[k] = 0;//回溯!!!!! 返回之前恢复现场
}
private static void show(int[] x) {
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[i]; j++) {
System.out.print((char)('A'+i));
}
}
System.out.println();
}
}
练习题三:有重复的组合问题。
X星球要派出一个5人组成的观察团前往W星。
其中:
A国最多可以派出4人。
B国最多可以派出2人。
C国最多可以派出2人。
D国最多可以派出1人。
E国最多可以派出1人。
F国最多可以派出3人。
那么最终派往W星的观察团会有多少种国别的不同组合呢?
/**
* 代表团出访,和有重复的组合本质是一样的。
* AAAA BB CC D E FFF
*/
public class Test12 {
public static void main(String[] args) {
int[] a = {4,2,2,1,1,3};
f(a,0,5,"");
}
/**
* @param a 可取最大个数的限定。
* @param k 当前考虑的位置。
* @param m 目标名额。
* @param s 已经决定的代表团成员。
*/
private static void f(int[] a, int k, int m, String s) {
if (k==a.length){
if (m==0)
System.out.println(s);
return;
}
String s2 = s;
for (int i = 0; i <= a[k]; i++) {
// m-i可能是负数,但是减少了代码了逻辑复杂度。
f(a, k+1,m-i, s2);
s2 = s2+(char)(k+'A');
}
}
}
【总结】
第三题的代码量少,但是效率不高,因为很多情况可以提前剪枝。