【问题标题】:recursion instead of multi-loops递归而不是多循环
【发布时间】:2010-09-17 11:57:22
【问题描述】:

我希望这个方法适用于任何给定数量的参数,我可以通过代码生成(有很多丑陋的代码)来做到这一点,它可以通过递归来完成吗?如果是这样怎么办?我理解递归,但我不知道怎么写。

private static void allCombinations(List<String>... lists) {
    if (lists.length == 3) {
        for (String s3 : lists[0]) {
            for (String s1 : lists[1]) {
                for (String s2 : lists[2]) {
                    System.out.println(s1 + "-" + s2 + "-" + s3);
                }
            }
        }
    }
    if (lists.length == 2) {
        for (String s3 : lists[0]) {
            for (String s1 : lists[1]) {
                    System.out.println(s1 + "-" + s3);
            }
        }
    }
}

【问题讨论】:

    标签: java recursion loops combinatorics


    【解决方案1】:

    你特别需要它是递归的吗?我会让它成为非递归的,但仍然不是特殊情况:

    public static void allCombinations(List<String>... lists) {
        int[] indexes = new int[lists.length];
    
        while (incrementIndexes(lists, indexes)) {
            StringBuilder builder = new StringBuilder();
            for (int i=0; i < indexes.length; i++) {
                if (i != 0) {
                    builder.append("-");
                }
                builder.append(lists[i].get(indexes[i]));
            }
            System.out.println(builder);
        }
    }
    
    private static boolean incrementIndexes(List<String>[] lists, int[] indexes) {
        for (int depth = indexes.length-1; depth >= 0; depth--) {
            indexes[depth]++;
            if (indexes[depth] != lists[depth].size()) {
                return true;
            }
            // Overflowed this index. Reset to 0 and backtrack
            indexes[depth] = 0;
        }
        // Everything is back to 0. Finished!
        return false;
    }
    

    【讨论】:

    • 在某种程度上,您已经模拟了递归解决方案无论如何都会做的事情(通过管理索引列表)。但是,我希望您的解决方案更有效,但可能比递归等效解决方案更具可读性。
    • 它确实有效地模拟了递归版本。在这种特殊情况下,Rasmus 给出的解决方案更简单,因为它很容易将“当前状态”捕获为字符串,然后附加到它。在需要我怀疑的每个当前项目的更一般情况下(续)
    • 无论如何,您都必须保留索引(或字符串)的集合-此时是否递归并没有太大区别。我会尝试想出另一种递归形式:)
    • 通常递归解决方案比非递归解决方案产生更多开销(主要是堆栈分配方式),但通常更容易阅读(Rasmus 的解决方案就是一个很好的例子)。
    【解决方案2】:

    这是一个简单的递归实现:

    private static void allCombinations(List<String>... lists) {
      allCombinations(lists, 0, "");
    }
    
    private static void allCombinations(List<String>[] lists, int index, String pre) {
      for (String s : lists[index]) {
        if (index < lists.length - 1) {
          allCombinations(lists, index + 1, pre + s + "-");
        }else{
          System.out.println(pre + s);
        }
      }
    }
    

    【讨论】:

    • 这并没有给出原始代码给出的结果。注意循环的 s3,s1,s2 顺序。
    • 啊,我认为这是一个错误。无论如何,这应该是递归解决方案的一般原则。您可以先对索引变量进行一些转换,然后再将其与 for 语句一起使用,以获得循环的另一种顺序。
    【解决方案3】:

    这是一个广义的递归版本。它抱怨在测试代码中创建未经检查的通用数组,但置换代码本身没问题:

    import java.util.*;
    
    public class Test
    {
        public interface Action<T> {
            void execute(Iterable<T> values);
        }
    
        public static void main(String[] args) {
            List<String> first = Arrays.asList(new String[]{"1", "2", "3"});
            List<String> second = Arrays.asList(new String[]{"a", "b", "c"});
            List<String> third = Arrays.asList(new String[]{"x", "y"});
            Action<String> action = new Action<String>() {
                @Override public void execute(Iterable<String> values) {
                     StringBuilder builder = new StringBuilder();
                     for (String value : values) {
                         if (builder.length() != 0) {
                             builder.append("-");
                         }
                         builder.append(value);
                     }
                     System.out.println(builder);
                }
            };
            permute(action, first, second, third);
        }
    
        public static <T> void permute(Action<T> action, Iterable<T>... lists) {
            Stack<T> current = new Stack<T>();
            permute(action, lists, 0, current);
        }
    
        public static <T> void permute(Action<T> action, Iterable<T>[] lists,
            int index, Stack<T> current) {
            for (T element : lists[index]) {
                current.push(element);
                if (index == lists.length-1) {
                  action.execute(current);
                } else {
                  permute(action, lists, index+1, current);
                }
                current.pop();
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      这是我的递归解决方案,其顺序正确,基于 Rasmus 的解决方案。它仅在所有列表大小相同时才有效。

      import java.util.Arrays;
      import java.util.List;
      
      
      public class Test {
      
              public static void main(String[] args) {
              List<String> first = Arrays.asList(new String[]{"1", "2", "3"});
              List<String> second = Arrays.asList(new String[]{"a", "b", "c"});
              List<String> third = Arrays.asList(new String[]{"x", "y", "z"});
              allCombinations (first, second, third);
              }
      
              private static void allCombinations(List<String>... lists) {
                    allCombinations(lists, 1, "");
              }
      
              private static void allCombinations(List<String>[] lists, int index, String pre) {
                  int nextHop = hop(index, lists.length-1);
                for (String s : lists[index]) {
                  if (index != 0) {
                    allCombinations(lists, nextHop, pre + s + "-");
                  } else System.out.println(pre + s);
                }
              }
              private static int hop(int prevIndex, int maxResult){
                  if (prevIndex%2 == 0){
                      return prevIndex-2;
                  } else {
                      if (prevIndex == maxResult) 
                          return prevIndex-1;
                      int nextHop = prevIndex+2;
                      if (nextHop > maxResult){
                          return maxResult;
                      } else return nextHop;
                  }
              }
      
      }
      

      允许不同大小列表的“正确排序”解决方案必须从最后一个列表开始并向后工作到第一个列表(列表 [0]),将元素附加到“ pre" 字符串并将其向前传递。同样,第一个列表将打印结果。我已经编写了代码,但是午餐已经准备好了,女朋友开始不喜欢 stackoverflow...

      【讨论】:

        猜你喜欢
        • 2021-03-20
        • 2018-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-28
        相关资源
        最近更新 更多