迭代数组列表

时间:2015-05-17 20:44:57

标签: java arrays recursion

我的设置如下:

List<int[]> list = new LinkedList<int[]>();
list.add(new int[] {1, 3, 4});
list.add(new int[] {4, 5});
list.add(new int[] {1, 4, 6});

编写代码时我不知道数组的大小。我试图遍历整个设置以生成所有可能的组合:

  

141
      144个
      146个
      151个
      154个
      156个
      341个
      ...

我目前正在使用递归来实现这个目标:

public static void recursive(List<int[]> list) {
    recursive(list, 0, "");
}

private static void recursive(List<int[]> list, int counter, String string)  {
    if (counter == list.size())
        System.out.println(string);
    else
        for (int i: list.get(counter))
            recursive(list, counter + 1, string + i);
}

我有两个问题:

  1. 我记得在一些讲座中听到递归总是可以用循环代替,但我不能为这种情况做这件事。这个循环版本怎么样?

  2. 有没有更好的方法来解决这个问题?

6 个答案:

答案 0 :(得分:6)

这是输出数组元素的所有组合的非递归方法。它绝对比递归解决方案更复杂。它的工作原理是将记录保存在一个补充数组中,该数组最近在列表中的每个数组中输出了该数字。

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class Iter {

    public static void main(String[] args) {
        List<int[]> list = new LinkedList<int[]>();
        list.add(new int[] { 1, 3, 4 });
        list.add(new int[] { 4, 5 });
        list.add(new int[] { 1, 4, 6 });

        iter(list);
    }

    private static void iter(List<int[]> list) {
        int[] index = new int[list.size()];
        Arrays.fill(index, 0);
        boolean done = false;

        do {
            // Output digits for this row
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)[index[i]]);
            }
            System.out.println();

            // Rollover digits, starting from last
            for (int j = list.size() - 1; j >= 0; j--) {
                index[j] = (index[j] + 1) % list.get(j).length;
                if (index[j] > 0) break;
                if (j == 0) done = true;
            }
        } while (!done);
    }

}

输出:

141
144
146
151
154
156
341
344
346
351
354
356
441
444
446
451
454
456

答案 1 :(得分:4)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

public class Test {
    public static <T> void combinations( final List<T[]> listOfArrays ){
        // Can't iterate a vanilla array so this is just a container for the
        // input converted to something Iterable.
        final ArrayList<List<T>> listOfIterables = new ArrayList<>();

        // Stack containing iterators indicating the current position within
        // the combination.
        final Stack<Iterator<T>> iterators = new Stack<>();

        // The current combination to output.
        final LinkedList<T> values = new LinkedList<>();

        final int len = listOfArrays.size();

        // Initialise the previous lists.
        for ( final T[] ts : listOfArrays ) {
            final List<T> l = Arrays.asList( ts );
            final Iterator<T> i = l.iterator();
            listOfIterables.add( l );
            iterators.push( i );
            values.addLast( i.next() );
        }

        while( true ){
            System.out.println( values );
            // Pop iterators that have finished and their corresponsing values.
            int i = len;
            while ( !iterators.isEmpty() && !iterators.peek().hasNext() ){
                iterators.pop();
                values.removeLast();
                i--;
            }
            // If all iterators are finished then we're done.
            if ( iterators.isEmpty() )
                return;
            // Increment to the next value in the combination.
            values.removeLast();
            values.add( iterators.peek().next() );
            // If iteraters were finished then replace them in the stack with
            // refreshed iterators.
            for ( ; i < len; i++ ){
                final Iterator<T> iterator = listOfIterables.get( i ).iterator();
                iterators.push( iterator );
                values.addLast( iterator.next() );
            }
        }
    }

    public static void main( String[] args ){
        List<Integer[]> list = new LinkedList<>();
        list.add( new Integer[]{ 1, 3, 4 } );
        list.add( new Integer[]{ 4, 5 } );
        list.add( new Integer[]{ 1, 4, 6 } );

        combinations( list );
    }
}

<强>输出

[1, 4, 1]
[1, 4, 4]
[1, 4, 6]
[1, 5, 1]
[1, 5, 4]
[1, 5, 6]
[3, 4, 1]
[3, 4, 4]
[3, 4, 6]
[3, 5, 1]
[3, 5, 4]
[3, 5, 6]
[4, 4, 1]
[4, 4, 4]
[4, 4, 6]
[4, 5, 1]
[4, 5, 4]
[4, 5, 6]

答案 2 :(得分:3)

好吧,如果您维护一个数组,一个列表或其他任何可以告诉您每个数组位置的内容,就可以在没有递归的情况下完成。

假设我们保留了这样的元素列表:

/**
 * Class to contain an index and a length. 
 */
private static class Pair {
    private int currIndex = 0;
    int length;

    /**
     * Constructor - we pass the length of the respective array.
     * This does not change during the lifetime of this program.
     * @param length The length of the respective array.
     */
    public Pair( int length ) {
        this.length = length;
    }

    /**
     * Increment the index by one. If we reach the length, start
     * from zero, and indicate that there is carry. That is, that
     * the next element will need to be updated.
     * @return True if next index down needs to be updated.
     */
    public boolean updateAndCheckCarry() {
        currIndex ++;
        if ( currIndex >= length ) {
            currIndex = 0;
            return true;
        }
        return false;
    }

    /**
     * Getter for the index itself
     * @return The current index.
     */
    public int getIndex() {
        return currIndex;
    }
}

我们的想法是,我们遍历每个数组,比如{4, 5}数组。我们从这四个开始,因为我们将通过循环,我们将更新到五个。但是上面的元素发生了变化,我们需要再次进入四个元素。这门课帮助我们做到了。

所以我们准备了索引列表:

/**
 * Prepare an index list, which for each element of the original list,
 * will contain a current index and the limit. This allows us to keep
 * track of which element we are in in every array.
 * 
 * @param listToIndex
 * @return The index list
 */
public static LinkedList<Pair> prepareIndexList(List<int[]> listToIndex) {
    LinkedList<Pair> result = new LinkedList<>();

    for ( int[] element : listToIndex ) {
        Pair item = new Pair(element.length);
        result.add(item);
    }
    return result;
}

这很简单 - 我们只需查看我们的列表并收集长度,以便我们以后能够知道何时将每个索引清零。

在每次迭代中,我们应该遍历列表并在每个数组的当前索引中打印数字。因此,如果第一个数组的索引为2,第二个数组为1,最后一个为0,我们将从您的示例中收集451

/**
 * Get the current value to print from the list. That is, go through the
 * list and collect the appropriate element from each array, into a string.
 * 
 * @param valuesList The list of integer arrays to go through
 * @param indexList  The list of current indices
 * @return String representing the collected current value.
 */
public static String getNextValue(List<int[]> valuesList, List<Pair> indexList) {
    StringBuilder sb = new StringBuilder(valuesList.size());
    Iterator<Pair> indexIter = indexList.iterator();
    for ( int[] element : valuesList ) {
        int index = indexIter.next().getIndex();
        sb.append(element[index]);
    }
    return sb.toString();
}

现在,这个解决方案真正的“肉”是索引的更新。这就像在数字中加1一样。想象一下,你有一个数字1958,你加1。它变为1959。现在再次添加1。因此,9变为0,您需要将1带到5.您现在拥有1960。保持这一点,你就可以达到1999。此时,你加1,将9加零,向左移动,然后将其归零,然后向左移动,然后将其归零,然后向左移动,然后进入2000。< / p>

以同样的方式 - 当我们需要携带1时从右边开始并经过左边 - 我们也更新了我们的索引列表:

/**
 * Update the index list. Starting from the end and going backwards, we
 * increment each index. Each index is zeroed if it gets past the respective
 * array size, and also returns true to indicate that the next level needs
 * to be updated as well.
 * 
 * @param indexList The list of indices to be updated
 * @return true if the updates bubbled all the way to the first element,
 *         and it, too, was zeroed. This means we have completed printing
 *         the tree.
 */
public static boolean updateIndexList(LinkedList<Pair> indexList) {
    Iterator<Pair> iter = indexList.descendingIterator();
    boolean hasCarry = true;

    while ( iter.hasNext() && hasCarry ) {
        hasCarry =  iter.next().updateAndCheckCarry();
    }

    return hasCarry;
}

如果我们从最左边的索引“携带” - 属于我们原始列表头部的索引 - 这意味着我们已经完成了程序,因为我们已经遍历了第一个数组中的所有元素。发生这种情况时,上述方法返回true。

现在我们只需要调用我们的方法:

    LinkedList indexList = prepareIndexList(list);
    boolean completed = false;

    while ( ! completed ) {
        System.out.println(getNextValue( list, indexList  ));
        completed = updateIndexList(indexList);
    }

答案 3 :(得分:2)

这是使用任何stacks / queues / linked list的解决方案没有。纯粹用简单的循环完成。我使用的唯一数据结构是一个1D阵列。

    int[] state = new int[list.size()];
    int incIdx = list.size()-1;     //increment index

    while(true){                
        for(int x=0; x<list.size(); x++)
            System.out.print(list.get(x)[state[x]);     
        System.out.println();                       
        state[list.size()-1]++;         //last one always increase

        while(state[incIdx] == list.get(incIdx).length){      //replaces back tracking      
            state[incIdx] = 0;
            incIdx--;               
                if(incIdx < 0) break; //solution found, exit loop
            state[incIdx]++;    
        }
        if(incIdx < 0) break; //solution found, exit loop
        incIdx = list.size()-1;
        if(state[list.size()-1] == list.get(list.size()-1).length)
            state[list.size()-1] = 0;           
    }
}

这个想法是使用一维数组来记住状态。用1D数组表示的状态,用于确定要打印的数组索引。

<强>输出:

141
144
146
151
154
156
341
344
346
351
354
356
441
444
446
451
454
456

答案 4 :(得分:1)

  

有没有更好的方法来解决这个问题?

更抽象的方法:

  1. 在列表的开头和结尾添加令牌。
  2. 构造一个格子,其中每个项目(列表中的标记或数组)都是一个级别。
  3. 打印出通过该格子的每条路径。
  4. 这可以解决您的问题,可以通过分布式系统列出每个可能的执行,其中进程总数等于列表中找到的最长数组的长度。

答案 5 :(得分:-3)

如果你的意思是迭代一个数组列表,那么这将打印出每个数组中的每个值:

for(int=0; i<list.size(); i++){
      for(int j=0; j<list.get(i).length; j++){
          System.out.println(j);
       }
    }