我的设置如下:
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);
}
我有两个问题:
我记得在一些讲座中听到递归总是可以用循环代替,但我不能为这种情况做这件事。这个循环版本怎么样?
有没有更好的方法来解决这个问题?
答案 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,我们将从您的示例中收集4
,5
和1
。
/**
* 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)
有没有更好的方法来解决这个问题?
更抽象的方法:
这可以解决您的问题,可以通过分布式系统列出每个可能的执行,其中进程总数等于列表中找到的最长数组的长度。
答案 5 :(得分:-3)
如果你的意思是迭代一个数组列表,那么这将打印出每个数组中的每个值:
for(int=0; i<list.size(); i++){
for(int j=0; j<list.get(i).length; j++){
System.out.println(j);
}
}