使用递归或任何其他解决方案替换嵌套的FOR循环

时间:2017-08-03 19:15:07

标签: java recursion nested-loops

<?xml version="1.0"?>
<Event>
 <Country>England</Country>
 <City>London</City>
 <City>Liverpool</City>  
 <Code>DWW</Code>  
 <ID>DG300</ID>
 <ID>SS500</ID>
 <Division>Europe</Division>
</Event>

假设我有一个像上面那样的XML。 我有一个方法,它采用不同数量的参数:

myMethod(String... params){}

参数是我想从XML读取的元素。 举个例子,我们可以从XML

中获取3个元素
myMethod(Country, City, ID){}

开始时,我会计算传递给此方法的参数数量:

int count=  params.size(); // let say for this example count=3

这里我创建一个以列表作为元素的数组:

List<String>[] tab= new ArrayList[count];

这里我迭代的次数与count参数相等,并在每个数组元素中放入一个列表:

for(int i=0; i<count; i++){
    tab[i] = new ArrayList<>();
}

在我的方法中间我有一些循环,它从XML读取元素并将它们放到数组中(将它们添加到列表中)。我使用JAVAX

最后我的数组看起来像这样

tab=[(England),(London,Liverpool),(DG300,SS500)]

现在我遇到了问题。我需要每个列表的笛卡尔,这意味着我需要行:

England London DG300
England London SS500
England Liverpool DG300
England Liverpool SS500

我可以使用像这样的嵌套循环

来做到这一点
for(int i=0; i< tab[0].size();i++){
    for(int j=0; i< tab[1].size();j++){
        for(int k=0; i< tab[2].size();k++){

        System.out.println(tab[0].get(i)+ " " + tab[1].get(j)+" "+tab[2].get(k))
}}}}}

但是这不是我在开头提到的好主意,我可以有不同数量的参数,所以我可以有不同数量的嵌套循环。它可以是两个但也可以是十个。

如何使用RECURSION替换此嵌套循环?或者也许我可以使用除递归以外的其他方式做到这一点?

4 个答案:

答案 0 :(得分:1)

这是一个解决方案。这是对我的其他帖子的回答的修改:how to use recursion for nested 'for' loops

public static void iterate(Object[] previousValues, List<Object>[] tabs) {
    if (tabs.length == 0) {
        System.out.println(Arrays.toString(previousValues));
    }
    else {
        final Object[] values = new Object[previousValues.length + 1];
        for (int i = 0; i < previousValues.length; i++) {
            values[i] = previousValues[i];
        }
        final List<Object>[] nextTabs = new List[tabs.length - 1];
        for (int i = 0; i < nextTabs.length; i++) {
            nextTabs[i] = tabs[i + 1];
        }
        for (Object o : tabs[0]) {
            values[values.length - 1] = o;
            iterate(values, nextTabs);
        }
    }
}
public static void iterate(List<Object>[] tabs) {
    iterate(new Object[0], tabs);
}

public static void main(String[] args) {
    final List<String> firstTab = new ArrayList<>();
    firstTab.add("England");
    final List<String> secondTab = new ArrayList<>();
    secondTab.add("London");
    secondTab.add("Liverpool");
    final List<String> thirdTab = new ArrayList<>();
    thirdTab.add("DG300");
    thirdTab.add("SS500");
    iterate(new List[]{firstTab, secondTab, thirdTab});
}

答案 1 :(得分:1)

建议您使用Guava的Lists.cartesianProduct方法:

openCon = new SqlConnection("Data Source=(LocalDB)\\v11.0;" +
                                       "AttachDbFilename=" + AppDomain.CurrentDomain.BaseDirectory + "Encounter.mdf;" +
                                       "Integrated Security=True");

输出:

  

结果:[[英格兰,伦敦,DG300],[英格兰,伦敦,SS500],[英格兰,廷布,DG300],[英格兰,廷布,SS500],[英格兰,利马,DG300],[英格兰,利马] ,SS500],[不丹,伦敦,DG300],[不丹,伦敦,SS500],[Bhutan,Thimphu,DG300],[不丹,廷布,SS500],[不丹,利马,DG300],[不丹,利马,SS500] ],[秘鲁,伦敦,DG300],[秘鲁,伦敦,SS500],[秘鲁,廷布,DG300],[秘鲁,廷布,SS500],[秘鲁,利马,DG300],[秘鲁,利马,SS500]]

应该注意的是,这个解决方案恰好是一行代码:

  • 更容易维护,并且对于继承您的代码的开发人员来说,可以更容易理解。
  • 未来的开发者不太可能被打破。
  • 几乎不值得进行单元测试,而提供的递归解决方案确实可以。
  • 你确实问过你是否可以使用除递归以外的其他方式做到这一点&#34;。

答案 2 :(得分:1)

假设您已设置列表以便tab [0]是要打印的第一个字符串的列表,tab [1]以下字符串等,您可以使用递归,如下所示:

void print_rec(List<String>[] tab, int depth, String str) {
    int maxdepth = tab.length - 1;
    for (int i = 0; i < tab[depth].size(); i++) { // number of strings at this depth
        if (depth == maxdepth) {
            System.out.println(str + tab[depth].get(i)); // print line
            // no more lists to go through, print the line
        } else {
            print_rec(tab, depth + 1, str + tab[depth].get(i) + " ");
            // add next entry at this level to str and recurse
        }
    }
    // done with this depth, go back up one
}

void printAll(List<Strings>[] tab) {
    print_rec(tab, 0, "");
}

这涵盖了与嵌套循环完全相同的所有条目。

答案 3 :(得分:0)

在编写递归代码时,我更喜欢先在Haskell中编写函数,然后将其转换回Java。对于我来说,Java对于大局来说太冗长了。

这是我的Haskell实现:

combinations :: [[String]] -> [[String]]
combinations []     = [[]]
combinations (x:xs) = do
    ys <- combinations xs
    y <- x
    return (y:ys)

快速测试以显示it works

λ> mapM_ print $ combinations [["England"],["London","Liverpool"],["DG300","SS500"]]
["England","London","DG300"]
["England","London","SS500"]
["England","Liverpool","DG300"]
["England","Liverpool","SS500"]

如果你不了解Haskell,那很好。这是combinations的Java实现:

// combinations :: [[String]] -> [[String]]
public static List<List<String>> combinations(List<List<String>> values) {

    // combinations [] = [[]]
    if (values.isEmpty()) {
        return Arrays.asList(Arrays.asList());
    }

    // combinations (x:xs) =
    List<String>       x  = values.get(0);
    List<List<String>> xs = values.subList(1, values.size());

    // do
    List<List<String>> outputs = new LinkedList<>();

    // ys <- combinations xs
    for (List<String> ys : combinations(xs)) {

        // y <- x
        for (String y : x) {

            // (y:ys)
            List<String> output = new LinkedList<>();
            output.add(y);
            output.addAll(ys);

            // return
            outputs.add(output);
        }
    }

    return outputs;
}

Ideone Demo

注意:这个Java代码非常低效,因为Haskell具有使递归和链表更有效的优化。优化此代码将是我对读者的挑战。我希望这将具有教育意义(并激励一些人learn Haskell)。