给出两个整数 n 和 k,写一个函数返回从 1......n 中选出的 k 个数的组合。
格式:
输入行依次输入一个整数 n 和一个整数 k,最后输出所有 k 个数的组合。
样例输入
n = 4
k = 2
样例输出
[ [ 2,4 ],[ 3,4 ],[ 2,3 ],[ 1,2 ],[ 1,3 ],[ 1,4 ]]
解题思路。
从1,2,3,4,5中取3个数字组合。其组合结果如下:
1,2,3
1,2,4
1,2,5
1,3,4
1,3,5
1,4,5
2,3,4
2,4,5
3,4,5
从以上可以发现一个规律,每个组合的数字都是3个,最左边的数字范围是从1到3(5-3+1),左边的数字固定之后,中间的数字范围是从"左边的数字+1"到4(5-3+2),中间的数字固定之后,最右边的数字就是从"中间的数字+1"到5(5-3+3);
那么就可以总结一个规律,从数字n中取m个,其实可以堪称有m层数字,从左往右,后面每一层的数字起始范围都是前面一层的数字范围+1,截止范围都是n-m+层数。
下面是组合的代码:
package suanfa;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
public class Combination {
public static void main(String[] args) {
long begin = System.currentTimeMillis();
List<String> list = compute(1, 5, 3, new ArrayList<String>(), null);
System.out.println("总共有" + list.size() + "个组合,共耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
print(list);
begin = System.currentTimeMillis();
Queue<String> queue = compute(5, 3);
System.out.println("总共有" + queue.size() + "个组合,共耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
print(queue);
}
public static void print(Collection<String> collection) {
Iterator<String> ite = collection.iterator();
while (ite.hasNext()) {
System.out.println(ite.next());
}
}
/**
* 从n个数字中取m个的组合。非递归算法
*
* @param n
* @param m
* @return
*/
public static Queue<String> compute(int n, int m) {
if (m > n) {
throw new RuntimeException("参数m不能大于n");
}
Queue<String> queue = new ArrayDeque<String>();
if (m == 1) {
for (int k = 1; k <= n; k++) {
queue.add("[" + k + "]");
}
}
int i = 1;
String str = null;
while (i <= m) {
if (i > 1) {
while (!queue.isEmpty()) {
// 这里表第i层的数据已经处理完,要处理下一层
if (queue.peek().split(",").length > i - 1) {
break;
}
str = queue.poll();
int startIndex = str.lastIndexOf(",");
if (startIndex < 0) {// 当i=2时,组合中还没有","。下标只能从1开始
startIndex = 1;
} else {
startIndex += 1;
}
for (int j = Integer.parseInt(str.substring(startIndex)) + 1; j <= n - m + i; j++) {
if (i == m) {
queue.add(str + "," + j + "]");
} else {
queue.add(str + "," + j);
}
}
}
i++;
} else {
for (int j = 1; j <= n - m + i; j++) {
queue.add("[" + j);
}
i++;
}
}
return queue;
}
/**
* 从n中取m个数字的组合。递归算法
*
* @param i 用于计数表示的是一个组合的第几层数数字。
* @param n
* @param m
* @param l 用于存放组合字符串
* @param str 暂存一个组合的字符串
*/
public static List<String> compute(int i, int n, int m, List<String> l, String str) {
if (m > n) {
throw new RuntimeException("参数m不能大于n");
}
if (m == 1) {
for (int k = 1; k <= n; k++) {
l.add("[" + k + "]");
}
} else if (i == m) {
int startIndex = str.lastIndexOf(",");
if (startIndex < 0) {
startIndex = 1;
} else {
startIndex += 1;
}
for (int j = Integer.parseInt(str.substring(startIndex)) + 1; j <= n - m + i; j++) {
l.add(str + "," + j + "]");
}
} else {
int j = i;
if (i != 1) {
int startIndex = str.lastIndexOf(",");
if (startIndex < 0) {
startIndex = 1;
} else {
startIndex += 1;
}
j = Integer.parseInt(str.substring(startIndex)) + 1;
}
for (; j <= n - m + i; j++) {
compute(i + 1, n, m, l, i == 1 ? ("[" + j) : (str + "," + j));
}
}
return l;
}
}
执行结果如下:
总共有10个组合,共耗时:0毫秒
[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]
总共有10个组合,共耗时:1毫秒
[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]