使用堆栈stack来实现取数组合,有这样一种需求,分别从10个集合中取一个数,组合成10个数是组合,不考虑排序,10几个集合每个集合包含3个数,
这个该如何实现呢?这个组合数有3的10次方,很多人会考虑使用for循环来实现,如下面的方法:
for(int j=0;j<3;j++){
int a1=arr[0][j];
for(int k=0;k<3;k++){
int a2=arr[1][k];
System.out.println("-->:"+ a1+" "+a2);
count++;
}
}
如果集合的个数小于3,使用3层for循环就可以了,如果是10个集合,那岂不是要10层for循环,可能你你会说10层也不算多,照样能实现。如果需求是20个集合、30个、100个集合呢?
你不可能写100层for循环代码吧,这是不现实的。
所以我们要使用java的堆栈的思想来实现。这里使用二维数组为代表集合,如int[6][3] arr 代表6个集合,每个集合3个元素。 使用一个索引数组来标识当前二维数组中遍历的索引位置
大概思路就是栈顶不断遍历,直到遍历当前集合所有元素,然后出栈,使下一个新的栈顶遍历完集合,直到栈中所有元素都是集合的最大索引时,则整个过程遍历完成。
为了更好理解,这里举2个集合,每个集合大小为3 ,最大索引为2 为例:
一开始都是从0开始遍历,入栈为 [0] ,元素未满,下一个集合继续入栈,[0]--[0,0] 判断栈顶0小于2,
开始遍历栈顶,[0,0]---0出栈-----[0]----1入栈--->[0,1]---1出栈--[0]--2入栈->[0,2] ,
此时栈顶为2,出栈,集合2遍历完成,不再入栈,[0,2]-- 2出栈--->[0]
开始集合1下一轮的遍历,[0]---0出栈-->[]---1入栈--[1],
元素未满,下一个集合继续入栈,[1]--[1,0] 判断栈顶0小于2,
开始遍历栈顶,[1,0]---0出栈-----[1]----1入栈--->[1,1]---1出栈--[1]--2入栈->[0,2] ,
此时栈顶为2,出栈,集合2遍历完成,不再入栈,[1,2]-- 2出栈--->[1]
开始集合1下一轮的遍历,[1]---1出栈-->[]---2入栈--[2],
元素未满,下一个集合继续入栈,[2]--[2,0] 判断栈顶0小于2,
开始遍历栈顶,[2,0]---0出栈-----[2]----1入栈--->[2,1]---1出栈--[2]--2入栈->[2,2] ,
此时栈顶为2,[2,2]---出栈-->[2],栈顶还是2,[2]继续出栈--->[]
此时栈已经为空了,所有组合已经遍历完成
这里的关键点就是,下轮循环时,栈顶后面的索引数组的值要复位为0
代码如下:
/**
* 分别取int[n][m] 二维数组中一个元素,组合成n这个数字,遍历每个子数组的m元素
*/
public static void comby(){
int[][] arr={
{2,4},
{9,10},
{14,16},
{21,22}
};
int width=arr[0].length;//二维数组的宽度
/**
* indexOfArr标记每个集合,即每个二维子数组当前遍历的索引,最大索引为 上面的width
*/
int[] indexOfArr =new int[arr.length];//标记二维数组,当前打印的索引,每组标记数组 的索引值 6组
for(int i=0;i<indexOfArr.length;i++){
indexOfArr[i]=0;
}
Stack<Integer> stack=new Stack<>();
stack.push(indexOfArr[0]);
//stack.push(n[1]);
List<Stack<Integer>>list=new ArrayList<>();
while(stack.size()>0){
/**
* 拼组合
*/
if(stack.size()<indexOfArr.length){
for(int i=stack.size();i<indexOfArr.length;i++){
stack.add(indexOfArr[i]);
}
}
//用来打印每种的堆栈
Stack<Integer> prstack=new Stack<>();
for(int i=0;i<stack.size();i++){
prstack.push(stack.get(i));
}
//只要栈顶小 于width-1 ,出栈
list.add(prstack);
//还未遍历完的,继续遍历
/*
* stack.peek()==width-1 表示当前集合已经遍历完成,出栈,改变前一个集合的索引遍历
* 如果栈中的所有元素都是width-1,表示所有集合都已经遍历完了,此时栈会被清空,也就遍历完了
* */
while(stack.size()>0&&stack.peek()==width-1){
stack.pop();
}
if(stack.size()>0&&stack.peek()<width-1){
indexOfArr[stack.size()-1]++;
stack.pop();
stack.push(indexOfArr[stack.size()]);
//后面的元素变成0开始
for(int i=stack.size();i<indexOfArr.length;i++){
indexOfArr[i]=0;
}
}
}
System.out.println("------分界线###########-------------");
System.out.println("-->:"+list);
System.out.println("-------分界线###########-------------");
System.out.println("-------打印组合-------------");
//打印所有的组合
for(Stack<Integer> s:list){
String str="";
for(int i=0;i<s.size();i++){
str=str+ arr[i][s.get(i)] +" ";
}
System.out.println();
System.out.println("组合-->:"+ str);
}
}
运行结果如下:

你可以定义改变二维数组的长度和宽度,定义30、100等,都可以全部遍历完组合