给定2D字符数组,找到从左上角到右下角的所有路径

时间:2014-07-31 20:35:10

标签: algorithm recursion big-o dynamic-programming

给定2D字符数组,找到从左上角到右下角的所有可能路径。我有以下递归解决方案。有人可以解释如何找到它的复杂性吗?此外,还有更优化的解决方案吗?我不太熟悉动态编程,但我认为它可以用来解决这个问题。

    public ArrayList<ArrayList<Character>> getPaths(char [][]grid){
        return getPaths(grid, 0, 0, new ArrayList<Character>());
    }

    public ArrayList<ArrayList<Character>> getPaths(char [][]grid, int x, int y, ArrayList<Character> path){
        ArrayList<ArrayList<Character>> allPaths = new ArrayList<ArrayList<Character>>();
        path.add(grid[x][y]);

        ArrayList<Character> path1 = new ArrayList<Character>(path);
        ArrayList<Character> path2 = new ArrayList<Character>(path);
        ArrayList<ArrayList<Character>> val1, val2;
        if(x == grid.length-1 && y == grid[0].length-1){
            allPaths.add(path);
        }
        else{
            if(x < grid.length-1){
                val1 = getPaths(grid, x+1, y, path1);
                for(ArrayList<Character> v1: val1)
                    allPaths.add(v1);
            }
            if(y < grid[0].length-1){
                val2 = getPaths(grid, x, y+1, path2);
                for(ArrayList<Character> v2: val2)
                    allPaths.add(v2);
            }
        }  
        return allPaths;
    }

3 个答案:

答案 0 :(得分:1)

如果您允许的举动只是向下或向右,那么考虑这个问题的另一种方法可能是y-1 downx-1 right的所有排列。例如,4x3网格将是:

ddrrr
drdrr
drrdr
...
rrrdd

在这种情况下,如果您更喜欢而不是递归,则可以使用任意数量的算法来生成“下一个词典排列”(在这种情况下,字符串也可以转换为二进制)以生成路径映射。要生成实际路径,您将从(0,0)开始并根据字符是向下还是向右指示更新x和y。

答案 1 :(得分:0)

我假设&#34;所有可能的路径&#34;你的意思是只左右移动,对吧?这就是您的代码似乎表明的内容。同样重要的是:你是想找到路径,还是只计算它们?

你几乎每次迭代都会进行两次分支,所以我说这是2 ^ n。但是你的答案大小也相对于网格的大小为2 ^ n。

在这里你应该如何考虑优化这个:想象一下网格中间的任意方格。它有一些引导来自的路径和一些引导的路径。目前,如果您接近之前已经接近过的广场,则您正在计算该广场的所有可能的退出路径。相反,对于每个正方形,一旦计算出其出口路径,您应该将与该正方形相关联的所有路径存储在内存中,以便下次进入路径找到通往该正方形的路径时,您不需要再次递归 - 您可以循环遍历所有退出路径并将当前的输入路径附加到它们。

请参阅:http://en.wikipedia.org/wiki/Memoization

答案 2 :(得分:0)

1)您的解决方案会搜索仅向左和向下移动的路径,因为如果允许任何路线和任何循环,路径可以有无穷大数量,我认为这是正确的。

2)时间复杂度: 您似乎只为每个路径生成一个节点,并且总操作数与总路径数乘以路径长度成比例。所有路径具有相同的长度(M + N)。我们还可以为路径计数构建递归公式:

K(M,N)= K(M - 1,N)+ K(M,N - 1)

K(1,*)= K(*,1)= 1

您可能会注意到它与二项式系数的递归公式非常相似,因此我们可以得出结论:

K(M,N)~C(M + N,N)

更确切地说:

K(M,N)= C(M + N-2,N-1)= C(M + N-2,M-1); K(0,0)= 0;

你可以检查这个方程如何将路径计数的递归公式转换为二项式系数和返回的一个,并且也可以更容易理解这些数字在Pascal树上的排列方式。 因此我们可以得出结论,时间复杂度为O(C(M + N,N)*(M + N))

3)显然你不能渐近地生成所有路径,因为结果的大小具有相同的复杂性,但如果你在通过同一个字符时不再生成所有子路径,可能会达到更好的性能,它是可能不容易递归,但你可以从右下角开始循环对角线,并将生成的子路径一个传递到顶部相邻,一个复制到左相邻角色。但正如我所说,它不会更好,因为复制可能比生成更快,但在这种情况下不是渐近。