正版包邮编程的逻辑如何面向对象
65.05元
包邮
(需用券)
去购买 >
这是一个再简单不过的组合问题:
编号 0-9 的 10 个球里面拿取任意 5 个,有多少种不同的组合?
答案是可以用公式算出来的,也就是 (10!) / ((5!) ^ 2) = 252 个。但是如果要把它们全部遍历出来呢?
下面是一种效率比较高的遍历方式,原理是将所有结果集看作是树节点(准确的说是叶子节点),然后去遍历这棵树即可。树的生成规则是:
一级节点的值是第一个球的编号,二级节点是第二个球的编号,依此类推;
任何一级节点的值必须大于上级节点的值。
这样能做到所有的叶子节点刚好覆盖所有的解,没有多余没有缺失。代码如下:
import java.util.Arrays;
import java.util.stream.IntStream;
/**
* 遍历组合树
*/
public class CombinationIterator {
public static void main(String[] args) {
int itemCount = 10; // 一共 10 个球
int pickCount = 5; // 取任意 5 个球
int answerCount = 0; // 可能的组合数量
// 生成第一个解,即 [0, 1, 2...]
int[] picks = IntStream.range(0, pickCount).toArray();
// 生成最后一个解
int[] lastPicks = IntStream.range(itemCount - pickCount, itemCount).toArray();
do {
if (picks != null) {
System.out.println(Arrays.toString(picks));
answerCount++;
}
picks = getNextPick(picks, lastPicks);
} while (picks != null);
System.out.printf("一共有 %d 个解。", answerCount);
}
/**
* 根据当前解计算下一个解,直到遇到最终解,则返回 null
*
* @param picks 当前解
* @param lastPicks 最终解
*
* @return 下一个解或 null
*/
private static int[] getNextPick(int[] picks, int[] lastPicks) {
if (Arrays.equals(picks, lastPicks)) {
return null;
}
int[] nextPick = Arrays.copyOf(picks, picks.length);
int movable = findMovable(nextPick, lastPicks);
nextPick[movable]++;
if (movable != nextPick.length - 1) {
// 将 movable 后面的点移回到贴紧 movable 的右侧
partialReset(nextPick, movable);
}
return nextPick;
}
// 在 picks 中寻找第一个可以右移的点
private static int findMovable(int[] picks, int[] lastPicks) {
for (int i = picks.length - 1; i >= 0; i--) {
if (picks[i] < lastPicks[i]) {
return i;
}
}
return -1; // 实际上不会返回 -1
}
// 指定位置后面的点都移回到贴紧该位置的右侧
private static void partialReset(int[] picks, int resetStart) {
for (int i = resetStart + 1; i < picks.length; i++) {
picks[i] = picks[i - 1] + 1;
}
}
}
原文链接:https://segmentfault.com/a/1190000013899418
java 11官方入门(第8版)教材
79.84元
包邮
(需用券)
去购买 >