组合算法
网上已经能够搜索到比较多的资料,大部分都是递归实现,因为递归实现是最优解,而且代码易于理解,
但是递归实现的风险——基数大的话可能出现栈溢出,所以这里使用循环实现;
假设现有 M=5 个数:5,9,12,50,45,从中取 N=3 个数做组合,将这 M 个数看做数组中的元素,提取成索引值,分别为:[0, 1, 2, 3, 4],首先穷举寻找规律,存在的组合情况如下:
[0, 1, 2] // 末尾自增
[0, 1, 3]
[0, 1, 4]
[0, 2, 3] // 类似于“逢10进1”,末尾自增
[0, 2, 4]
[0, 3, 4] // 类似于“逢10进1”,末尾自增
[1, 2, 3] // 类似于“逢10进1”,末尾自增
[1, 2, 4]
[1, 3, 4]
[2, 3, 4] // 类似于“逢10进1”,末尾自增
规律基本明了,只是每个位置上的进位判断值不一样。代码实现如下:
import org.junit.jupiter.api.Test;
/**
* 组合算法
*/
public class ZuHeTest {
@Test
public void zuhe() {
int m = 6;
int n = 3;
// select用于存放组合的索引,初始化, 初始化后的就是1组
int[] select = new int[n];
for (int i = 0; i < n; i++) {
select[i] = i;
}
printArr(select); // 输出一种情况
// lastIndex 指针,指向哪个元素,该元素就做自增
int lastIndex = select.length - 1;
// 进位判断值
int judgeVal = m;
// 触发进位标记
boolean trigger = false;
while (lastIndex >= 0) {
// 指针指向的元素索引自增
int val = select[lastIndex] + 1;
if (val < judgeVal) {
select[lastIndex] = val;
while (trigger && lastIndex < select.length - 1) {
// 上依次循环已经触发进位机制(上一行代码完成的进位),并将后面的数字初始化
// 例如:[0,1,4],进位后为 [0,2,4],需要将 4 初始化为 3,变为:[0,2,3]
lastIndex++;
val++;
select[lastIndex] = val;
judgeVal++;
}
trigger = false;
printArr(select); // 输出
} else {
// 自增值到达边界,指针和判断值往前挪
lastIndex--;
judgeVal--;
// 触发进位标记
trigger = true;
}
}
}
private void printArr(int[] arr) {
for (int i : arr) {
System.out.print(i);
System.out.print(',');
}
System.out.println();
}
}
循环实现确实没有递归的好理解。
(排列算法后续添加)