【问题标题】:How to optimize this recursive backtracking algorithm?如何优化这种递归回溯算法?
【发布时间】:2019-10-25 01:11:03
【问题描述】:

我对递归回溯非常陌生,但我想我会尝试一下。我编写了这段代码,它找到从低到高(作为参数传入)的整数的所有排列,这些排列总和为给定的 int,NumTotal。它适用于较小的数字,但我得到了

线程“主”java.lang.OutOfMemoryError 中的异常:超出 GC 开销限制

对于更大的数字。

public static void findAllSolutionMethod(int NTotal, ArrayList<ArrayList<Integer>> solutions,
        ArrayList<Integer> currentSolution, int lower, int upper) {

    // success base case
    if (NTotal == 0) {
        ArrayList<Integer> copy = new ArrayList<Integer>();
        // creates deep copy 
        for (int i = 0; i < currentSolution.size(); i++) {
            copy.add(currentSolution.get(i));
        }
        // add to solutions arraylist
        solutions.add(copy);
        return;
    }

    // invalid number base case (number added too big)
    else if (NTotal < 0) {
        return;
    }

    else {
        // iterates through range of numbers
        for (int i = lower; i <= upper; i++) {
            currentSolution.add(i);
            findAllSolutionMethod(NTotal - i, solutions, currentSolution, lower, upper);
            currentSolution.remove(currentSolution.size() - 1);
        }
    }
}

有什么办法可以优化这段代码,使其不占用太多空间?

【问题讨论】:

  • solutions.add(new ArrayList&lt;&gt;(currentSolution)); 将是“创建深层副本”的更简单方法。它也可能更节省内存,因为它可以预先调整支持列表副本的数组大小。

标签: java recursion permutation


【解决方案1】:

如果您将图像递归调用为树,这很容易。将解决方案写在纸上,制作bfs,即可看到优化方式。

public static List<int[]> findPermutations(int sum, int low, int high) {
    final Function<Node, int[]> getPath = node -> {
        int[] arr = new int[node.depth()];
        int i = arr.length - 1;

        while (node != null) {
            arr[i--] = node.val;
            node = node.parent;
        }

        return arr;
    };

    List<int[]> res = new LinkedList<>();
    Deque<Node> queue = new LinkedList<>();

    for (int i = low; i <= high; i++) {
        queue.clear();
        queue.add(new Node(low));

        while (!queue.isEmpty()) {
            Node node = queue.remove();

            if (node.sum == sum)
                res.add(getPath.apply(node));
            else {
                for (int j = low; j <= high; j++) {
                    if (node.sum + j <= sum)
                        queue.add(new Node(j, node.sum + j, node));
                    else
                        break;
                }
            }
        }
    }

    return res;
}

private static final class Node {

    private final int val;
    private final int sum;
    private final Node parent;

    public Node(int val) {
        this(val, val, null);
    }

    public Node(int val, int sum, Node parent) {
        this.val = val;
        this.sum = sum;
        this.parent = parent;
    }

    public int depth() {
        return parent == null ? 1 : (parent.depth() + 1);
    }

    @Override
    public String toString() {
        return val + " (" + sum + ')';
    }

}

演示:

findPermutations(4, 1, 3).forEach(path -> System.out.println(Arrays.toString(path)));

输出:

[1, 3]
[1, 1, 2]
[1, 2, 1]
[1, 1, 1, 1]
[2, 2]
[2, 1, 1]
[3, 1]

【讨论】:

  • 从空间复杂度的角度来看,使用deque代替arraylist有什么好处?
  • 我明白了,像一棵树一样接近它是有道理的。出于某种原因,我的输出不正确。我只得到 [1, 3] [1, 1, 2] [1, 2, 1] [1, 1, 1, 1] 并且它打印了 3 次。
猜你喜欢
  • 2019-12-21
  • 2021-04-28
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 2018-05-02
  • 2015-01-21
  • 2018-10-08
  • 2019-10-11
相关资源
最近更新 更多