[英]Iterating over list of arrays
我有一個看起來像這樣的設置:
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);
}
我有兩個問題:
我記得在一些講座中聽到遞歸總是可以用循環代替,但我不能為這種情況做這件事。 這個循環版本怎么樣?
有沒有更好的方法來解決這個問題?
這是一個輸出所有數組元素組合的非遞歸方法。 它絕對比遞歸解決方案更復雜。 它的工作原理是將記錄保存在一個補充數組中,該數組最近在列表中的每個數組中輸出了該數字。
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
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]
好吧,它可以在沒有遞歸的情況下完成,如果你維護一個數組,一個列表或其他任何告訴你在每個數組中的位置。
假設我們保留了這樣的元素列表:
/**
* 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
。
以同樣的方式 - 從右邊開始,當我們需要攜帶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);
}
這是沒有使用任何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數組表示的狀態,以確定要打印的數組索引。
OUTPUT:
141
144
146
151
154
156
341
344
346
351
354
356
441
444
446
451
454
456
有沒有更好的方法來解決這個問題?
一種更抽象的方法:
這可以解決您的問題,可以通過分布式系統列出每個可能的執行,其中進程總數等於列表中找到的最長數組的長度。
如果你的意思是迭代一個數組列表,那么這將打印出每個數組中的每個值:
for(int=0; i<list.size(); i++){
for(int j=0; j<list.get(i).length; j++){
System.out.println(j);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.