使用递归打印长度X的所有组合

时间:2019-06-09 15:22:13

标签: java recursion combinations

示例

给出数组[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]的情况为例。

  1. PrintCombo(a,buffer,0,0)
  2. buffer [0]更新为1
  3. 我们称PrintCombo(a,buffer,1,1)
  4. buffer [1]更新为2
  5. 我们称PrintCombo(a,buffer,2,2)
  6. buffer [2]更新为3
  7. 我们称为PrintCombo(a,buffer,3,3)
  8. 由于buffer.length == bufferIndex,我们称为printArray。
  9. 我们返回上一个通话

这是我迷路的地方。堆栈如何进行先前的调用?由于我不喜欢记住解决方案,因此我正在努力彻底理解这种方法。

我决定通过添加打印语句来编辑代码,以查看每次迭代时缓冲区内部的内容。这是我打印的内容,例如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

2 个答案:

答案 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中的一个对象),并在添加结果之前检查结果是否已在数组中,或者返回整个结果数组并在生成数组后删除重复项。 (尽管第一个会提高内存效率)