示例
给出数组[1,2,3]或[1,2,3,4],打印长度为3的所有唯一组合。
代码
public class PrintCombo {
public void printCombo(int [] a, int [] buffer, int startIndex, int bufferIndex){
printArray(buffer);
if(buffer.length == bufferIndex){
System.out.println();
System.out.println("SOLUTION START");
printArray(buffer);
System.out.println("SOLUTION END");
System.out.println();
return;
}
if(startIndex == a.length){
return;
}
for(int i = startIndex; i<a.length; i++){
buffer[bufferIndex] = a[i];
printCombo(a,buffer,i+1,bufferIndex+1);
}
}
public void printArray(int [] buffer){
for(int i = 0; i<buffer.length; i++){
System.out.print(" "+buffer[i]);
}
System.out.println();
}
}
输出
对于数组[1,2,3] ==> 1,2,3
对于数组[1,2,3,4] ==> 1,2,3 || 1,2,4 || 1,3,4 || 2,3,4
问题
我已经花了3个小时使用调试器来跟踪代码,但我仍在努力理解递归逻辑的工作方式。
例如,让我们以数组为[1,2,3]的情况为例。
这是我迷路的地方。堆栈如何进行先前的调用?由于我不喜欢记住解决方案,因此我正在努力彻底理解这种方法。
我决定通过添加打印语句来编辑代码,以查看每次迭代时缓冲区内部的内容。这是我打印的内容,例如a = [1,2,3],缓冲区大小为3。
0 0 0
1 0 0
1 2 0
1 2 3
SOLUTION START
1 2 3
SOLUTION END
1 3 3
2 3 3
2 3 3
3 3 3
答案 0 :(得分:3)
最初在printCombo
中调用for循环时,将在每次迭代中将buffer
的第一个元素设置为所有可能的值:
[1,-,-] // i = 0, first iteration
[2,-,-] // i = 1, second iteration
[3,-,-] // i = 2, ...
[4,-,-] // i = 3, ...
对于这些迭代中的每个迭代,都对printCombo
进行递归调用,以为buffer
中的其余元素创建所有可能的组合。例如。在第一次迭代中,[1,_,_]
被传递给printCombo
,后者的for循环现在也将第二个元素设置为所有可能的值:
[1,2,-] // i = 0, first iteration in first recursive call to printCombo
[1,3,-] // i = 1, second iteration in first recursive call to printCombo
[1,4,_] // i = 2, ...
该过程一直持续到buffer
已满(第一个if
条件)或可能的值池耗尽(第二个if
条件)。在第一种情况下,找到并打印了候选人。在第二种情况下,到达死胡同。
这是buffer
随时间的演变,其中缩进级别对应于递归深度(a = [1,2,3,4]
和缓冲区大小3
):
[1,-,-]
[1,2,-]
[1,2,3] // buffer.length == bufferIndex -> printArray
[1,2,4] // buffer.length == bufferIndex -> printArray
[1,3,-]
[1,3,4] // buffer.length == bufferIndex -> printArray
[1,4,-] // startIndex == a.length -> return
[2,-,-]
[2,3,-]
[2,3,4] // buffer.length == bufferIndex -> printArray
[2,4,-] // startIndex == a.length -> return
[3,-,-]
[3,4,-] // startIndex == a.length -> return
[4,-,-] // startIndex == a.length -> return
答案 1 :(得分:2)
您可以考虑采用这种方法:每个组合都是从初始数组中删除一个值直到达到所需长度的结果。
如果以该数组为例[1, 2, 3, 4, 5]
,则首先要一次从其中删除1个值,然后得到以下结果数组:
[2, 3, 4, 5] //remove index [0]
[1, 3, 4, 5] //remove index [1]
[1, 2, 4, 5] //remove index [2]
[1, 2, 3, 5] //remove index [3]
[1, 2, 3, 4] //remove index [4]
然后您可以从其中的每一个中删除一个值,以达到所需的长度3。[2, 3, 4, 5]
随后将为您提供以下数组
[3, 4, 5] //remove index [0]
[2, 4, 5] //remove index [1]
[2, 3, 5] //remove index [2]
[2, 3, 4] //remove index [3]
以此类推...请注意,此 会生成重复的数组,所以我的建议是通过 final array 引用(java中的一个对象),并在添加结果之前检查结果是否已在数组中,或者返回整个结果数组并在生成数组后删除重复项。 (尽管第一个会提高内存效率)