将此递归转换为迭代

时间:2013-06-11 23:18:40

标签: java

我有这段代码以递归方式获取字符串中所有字符串的排列:

public static List<List<String>> combinations(List<String> strings)
{
    if (strings.size() > 1)
    {
        List<List<String>> result = new ArrayList<List<String>>();

        for (String str : strings)
        {
            List<String> subStrings = new ArrayList<String>(strings);
            subStrings.remove(str);

            result.add(new ArrayList<String>(Arrays.asList(str)));

            for (List<String> combos : combinations(subStrings))
            {
                combos.add(str);
                result.add(combos);
            }
        }

        return result;
    }
    else
    {
        List<List<String>> result = new ArrayList<List<String>>();
        result.add(new ArrayList<String>(strings));
        return result;
    }
}

如果我的arraylist包含太多值,它会溢出堆栈。我已经知道将算法从递归转换为迭代会帮助我解决这个内存问题,因为我会在堆上自己处理堆栈而不是使用本机堆栈。我以前从未这样做过,也无法解决如何解决这个问题。我的问题并不像看到这种转变的例子那么简单,所以我可以非常感谢有关如何实现这一目标的一些提示。

2 个答案:

答案 0 :(得分:1)

如下所示,使用特殊的WorkPair类来模拟将在递归方法中创建的堆栈帧。通常,当转换为迭代方法时,您可能需要创建一个存储部分工作的数据结构,就像递归方法一样,此信息存储在堆栈中(我们没有)。

private static class WorkPair{

  public final List<String> so_far;
  public final List<String> left;
  public WorkPair(List<String> sf,List<String> l){
    so_far=sf;left=l;
  }

}

public static List<List<String>> combinationsIterative(List<String> strings)
{

    Queue<WorkPair> queue = new LinkedList<WorkPair>();

        // setup queue
        for(String str : strings){
           List<String> so_far = new ArrayList<String>(Arrays.asList(str));
           List<String> left = new ArrayList<String>(strings);
           left.remove(str);
           queue.add(new WorkPair(so_far,left));
        }

        // collect the results
        List<List<String>> result = new ArrayList<List<String>>();            
        while(!queue.isEmpty()){
          WorkPair pair = queue.remove();
          // at each stage add the list so_far
          List<String> so_far_add = new ArrayList<String>(pair.so_far);
          result.add(so_far_add);

          // if there's more work to be done create more workpairs
          if(pair.left.size()>0){
            // push work items for so_far + one element of left
            for(String str : pair.left){
                   List<String> so_far = new ArrayList<String>(pair.so_far);
                   so_far.add(str);
                   List<String> left = new ArrayList<String>(pair.left);
                   left.remove(str);
                   queue.add(new WorkPair(so_far,left));      
            }
          }

        }

    return result;
}

答案 1 :(得分:1)

你基本上需要的是迭代的powerset结构。查看this sample code (section 'Iterative')以了解如何使用Java完成集合任务。如果在组合原始字符串的给定子集时需要区分不同的顺序,则必须在第二个处理步骤中生成每个powerset元素的所有排列。 This page将帮助您开始实现它(使用代码为您提供索引1..<list_size>的所有排列,添加一个简单的映射来获取有序的字符串列表。)

但是,请考虑您是否确实需要字符串集的显式表示。您可以等效地使用k中的数字1..<original set size>作为位向量,位#i指示是否包含源列表中位置i的字符串。在有序组合的情况下,排列将由k及其1位的数量唯一确定。

由于排列是可排序的,您实际上只需要2个数字+原始列表来唯一标识结果的每个元素,并轻松重建实际的字符串组合。

This SO question也可能对你感兴趣。