在 Java 中将可变长度整数数组倒数到零

Counting down a variable length array of integers to Zero in Java

我有一个数组,其中包含 3 个文件中的总行数。示例:[3,4,5]。我想生成一个数字序列,以有条不紊的方式将该数组计数为零,为我提供三个文件中的每行组合。此示例使用 3 files/length-3 数组,但该算法应该能够处理任意长度的数组。

对于上面的示例,解决方案如下所示:

[3,4,5]  (line 3 from file 1, line 4 from file 2, line 5 from file 3)  
[3,4,4]  
[3,4,3]  
[3,4,2]  
[3,4,1]  
[3,4,0]  
[3,3,5]  
[3,3,4]  
[3,3,3]  
[3,3,2]  

等等...

我第一次尝试为此递归递减数组中的一个位置,并在该位置达到零时递减它之前的位置。但是,我无法让递减比最后两个位置更远。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

    public class FilePositionGenerator {

        public static void main(String[] args) {
            int[] starterArray = {2, 2, 2};
            int[] counters = starterArray.clone();
            List<Integer> results = new ArrayList<Integer>();

            FilePositionGenerator f = new FilePositionGenerator();
            f.generateFilePositions(starterArray, counters, (starterArray.length - 1), results);
        }//end main

        void generateFilePositions(int[] originalArray, int[] modifiedArray, int counterPosition, List<Integer> results) {

            if (modifiedArray[counterPosition] == 0 && counterPosition > 0) {
                modifiedArray[counterPosition] = originalArray[counterPosition];
                counterPosition = counterPosition - 1;
            } else {
                modifiedArray[counterPosition] = modifiedArray[counterPosition] - 1;
                System.out.println(Arrays.toString(modifiedArray));
                generateFilePositions(originalArray, modifiedArray, counterPosition, results);
            }
        }
    }  

我知道要处理变长数组,算法必须是递归的,但我想不通。所以我决定尝试一种不同的方法。

我第二次尝试生成算法使用双指针方法,该方法将指针保持在当前倒计时位置[最右边的位置],以及指向下一个非最右边位置(pivotPointer)的指针,当最右边的位置达到零。像这样:

import java.util.Arrays;

class DualPointer {
    public static void main(String[] args) {
        int[] counters = {2, 2, 2}; // initialize the problem set
        int[] original = {2, 2, 2}; // clone a copy to reset the problem array
        int[] stopConditionArray = {0, 0, 0};   // initialize an object to show what the stopCondition should be
        int pivotLocation = counters.length - 1;    // pointer that starts at the right, and moves left
        int counterLocation = counters.length - 1;  // pointer that always points to the rightmost position
        boolean stopCondition = false;

        System.out.println(Arrays.toString(counters));
        while (stopCondition == false) {

            if (pivotLocation >= 0 && counterLocation >= 0 && counters[counterLocation] > 0) {
                // decrement the rightmost position
                counters[counterLocation] = counters[counterLocation] - 1;
                System.out.println(Arrays.toString(counters));
            } else if (pivotLocation >= 0 && counters[counterLocation] <= 0) {
                // the rightmost position has reached zero, so check the pivotPointer
                // and decrement if necessary, or move pointer to the left
                if (counters[pivotLocation] == 0) {
                    counters[pivotLocation] = original[pivotLocation];
                    pivotLocation--;
                }
                counters[pivotLocation] = counters[pivotLocation] - 1;
                counters[counterLocation] = original[counterLocation]; // reset the rightmost position
                System.out.println(Arrays.toString(counters));
            } else if (Arrays.equals(counters, stopConditionArray)) {
                // check if we have reached the solution
                stopCondition = true;
            } else {
                // emergency breakout of infinite loop
                stopCondition = true;
            }

        }
    }
}

在 运行 上,您可以看到 2 个明显的问题:

[2, 2, 2]  
[2, 2, 1]  
[2, 2, 0]  
[2, 1, 2]  
[2, 1, 1]  
[2, 1, 0]  
[2, 0, 2]  
[2, 0, 1]  
[2, 0, 0]  
[1, 2, 2]  
[1, 2, 1]  
[1, 2, 0]  
[0, 2, 2]  
[0, 2, 1]  
[0, 2, 0]  

第一,当 pivotPointer 和 currentCountdown 相隔超过一个数组单元格时,pivotPointer 不会正确递减。其次,在 counters[pivotLocation] = counters[pivotLocation] - 1; 行有一个 arrayIndexOutOfBounds 如果固定的话, 将算法从 运行 中完全正确地分解出来。

如有任何帮助,我们将不胜感激。

我会建议一个不同的方法。

递归的想法是减少每次递归调用中问题的大小,直到你达到一个微不足道的情况,在这种情况下你不必进行另一个递归调用。

当你第一次调用n个元素数组的递归方法时,你可以循环迭代最后一个索引(n-1)的值范围,进行递归调用以生成所有组合前 n-1 个元素的数组,并组合输出。

下面是部分 Java/ 部分伪代码 :

第一次通话:

List<int[]> output = generateCombinations(inputArray,inputArray.length);

递归方法List<int[]> generateCombinations(int[] array, int length):

List<int[]> output = new ArrayList<int[]>();
if length == 0 
    // the end of the recursion
    for (int i = array[length]; i>=0; i--)
        output.add (i)
else
    // the recursive step
    List<int[]> partialOutput = generateCombinations(array, length - 1)
    for (int i = array[length]; i>=0; i--)
        for (int[] arr : partialOutput)
            output.add(arr + i)
return output

递归方法returns一个List<int[]>。这意味着在 "output.add (i)" 中,您应该创建一个包含单个元素的 int 数组并将其添加到列表中,而在 output.add(arr + i) 中,您将创建一个包含 arr.length+1 个元素的数组,并且将 arr 的元素复制到它,然后是 i.

递归的有趣之处在于你可以让它完全按照你的意愿去做。在这种情况下,对于计数数组索引 i 处的每个数字,我们希望将其与索引 i + 1 处的每个数字组合,直到到达数组末尾。诀窍是不要 return 索引 i 一旦你 运行 通过它的所有选项。

JavaScript代码:

var arr = [3,4,5];
var n = arr.length;

function f(cs,i){
  // base case
  if (i == n){
    console.log(cs.join(','));
    return;
  }

  // otherwise
  while (cs[i] >= 0){
    // copy counts
    _cs = cs.slice();

    // recurse
    f(_cs,i + 1);

    // change number at index i
    cs[i]--;
  }
}

f(arr,0);