关于java:用于查找NxN网格中所有路径的算法

Algorithm for finding all paths in a NxN grid

想象一下,一个机器人坐在NxN网格的左上角。 机器人只能在两个方向上移动:向右和向下。 机器人有多少条可能的路径?

我可以在Google上找到解决此问题的方法,但是我对这些解释并不十分清楚。 我试图清楚地了解有关如何解决此问题并在Java中实现的逻辑。 任何帮助表示赞赏。

更新:这是一个面试问题。 现在,我试图到达右下角并打印可能的路径。


1
2
3
4
5
6
7
8
9
10
11
public static int computePaths(int n){
    return recursive(n, 1, 1);      
}  

public static int recursive(int n, int i, int j){
    if( i == n || j == n){
        //reach either border, only one path
        return 1;
    }
    return recursive(n, i + 1, j) + recursive(n, i, j + 1);
}

查找所有可能的路径:
仍然使用递归方法。路径变量的开头是"",然后将访问的每个点添加到"路径"。到达(n,n)点时会形成一条可能的路径,然后将其添加到列表中。
每个路径都表示为字符串,例如"(1,1)(2,1)(3,1)(4,1)(4,2)(4,3)(4,4)"。所有可能的路径都存储在字符串列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static List<String> robotPaths(int n){
    List<String> pathList = new ArrayList<String>();
    getPaths(n, 1,1,"", pathList);
    return pathList;
}
public static void getPaths(int n, int i, int j, String path, List<String> pathList){
    path += String.format(" (%d,%d)", i , j);
    if( i ==n && j == n){ //reach the (n,n) point
        pathList.add(path);
    }else if( i > n || j > n){//wrong way
        return;
    }else {
        getPaths(n, i +1, j , path, pathList);
        getPaths(n, i , j +1, path, pathList);
    }
}


我认为您的问题中没有障碍的迹象,因此我们可以假设没有障碍。

请注意,对于n + 1 x n + 1网格,机器人需要精确地执行2n步才能到达右下角。因此,它只能进行2n个动作。

让我们从一个简单的案例开始:[找到右下角的所有路径]

机器人可以精确地执行choose(n,2n) = (2n)!/(n!*n!)路径:只需要选择2n个移动中的哪一个是正确的,其余的都按下即可(确切地说是这些n)。
要生成可能的路径:只需生成大小为2n且正好为n 1的所有二进制向量。 1表示向右移动,0表示向下移动。

现在,让我们将其扩展到所有路径:
首先选择路径的长度。为此,请遍历所有可能性:0 <= i <= 2n,其中i是路径的长度。在此路径中,有max(0,i-n) <= j <= min(i,n)个正确的步骤。
要生成所有可能性,请实现以下伪代码:

1
2
3
for each i in [0,2n]:
  for each j in [max(0,i-n),min(i,n)]:
    print all binary vectors of size i with exactly j bits set to 1

注1:打印大小为i且j位设置为1的所有二进制矢量可能在计算上很昂贵。这是可以预期的,因为存在成倍的解决方案。
注2:对于情况i=2n,您将得到j in [n,n],如预期的那样(上述更简单的情况)。


这是因为如果机器人可以向4个方向移动,而不仅仅是2个方向,但是下面的递归解决方案(使用Javascript)有效,并且我尝试使其尽可能清晰:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//first make a function to create the board as an array of arrays
var makeBoard = function(n) {
  var board = [];
  for (var i = 0; i < n; i++) {
    board.push([]);
    for (var j = 0; j < n; j++) {
      board[i].push(false);
    }
  }
  board.togglePiece = function(i, j) {
    this[i][j] = !this[i][j];
  }
  board.hasBeenVisited = function(i, j) {
    return !!this[i][j];
  }
  board.exists = function(i, j) {
    return i < n && i > -1 && j < n && j > -1;
  }
  board.viablePosition = function(i, j) {
    return board.exists(i, j) && !board.hasBeenVisited(i,j);
  }
  return board;
};


var robotPaths = function(n) {
  var numPaths = 0;
  //call our recursive function (defined below) with a blank board of nxn, with the starting position as (0, 0)
  traversePaths(makeBoard(n), 0, 0);

  //define the recursive function we'll use
  function traversePaths(board, i, j) {
    //BASE CASE: if reached (n - 1, n - 1), count as solution and stop doing work
    if (i === (n - 1) && j === (n - 1)) {
      numPaths++;
      return;
    }
    //mark the current position as having been visited. Doing this after the check for BASE CASE because you don't want to turn the target position (i.e. when you've found a solution) to true or else future paths will see it as an unviable position
    board.togglePiece(i, j);

    //RECURSIVE CASE: if next point is a viable position, go there and make the same decision

    //go right if possible
    if (board.viablePosition(i, j + 1)) {
      traversePaths(board, i, j + 1);
    }

    //go left if possible
    if (board.viablePosition(i, j - 1)) {
      traversePaths(board, i, j - 1);
    }

    //go down if possible
    if (board.viablePosition(i + 1, j)) {
      traversePaths(board, i + 1, j);
    }

    //go up if possible
    if (board.viablePosition(i - 1, j)) {
      traversePaths(board, i - 1, j);
    }

    //reset the board back to the way you found it after you've gone forward so that other paths can see it as a viable position for their routes
    board.togglePiece(i, j);
  }
  return numPaths;
};

较干净的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var robotPaths = function(n, board, i, j) {
    board = board || makeBoard(n),
    i = i || 0,
    j = j || 0;

    // If current cell has been visited on this path or doesn't exist, can't go there, so do nothing (no need to return since there are no more recursive calls below this)
    if (!board.viablePosition(i, j)) return 0;
    // If reached the end, add to numPaths and stop recursing
    if (i === (n - 1) && j === (n - 1)) return 1;
    // Mark current cell as having been visited for this path
    board.togglePiece(i, j);
    // Check each of the four possible directions
    var numPaths = robotPaths(n, board, i + 1, j) + robotPaths(n, board, i - 1, j) + robotPaths(n, board, i, j + 1) + robotPaths(n, board, i, j - 1);
    // Reset current cell so other paths can go there (since board is a pointer to an array that every path is accessing)
    board.togglePiece(i, j);
    return numPaths;
}

所以:

1
robotPaths(5); //returns 8512

https://math.stackexchange.com/questions/104032/finding-points-in-a-grid-with-exactly-k-paths-to-them-在这里查看我的解决方案。似乎正是您所需要的(是的,语句略有不同,但通常情况下它们是相同的)。


以下是Java中的代码,用于计算从NXN矩阵的左上角到右下角的所有可能路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class paths_in_matrix {

    /**
     * @param args
     */

    static int n=5;
    private boolean[][] board=new boolean[n][n];
    int numPaths=0;
    paths_in_matrix(){
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                board[i][j]=false;
            }
        }
    }
    private void togglePiece(int i,int j){
        this.board[i][j]=!this.board[i][j];
    }
    private boolean hasBeenVisited(int i,int j){
        return this.board[i][j];
    }
    private boolean exists(int i,int j){
        return i < n && i > -1 && j < n && j > -1;
    }
    private boolean viablePosition(int i,int j){
        return exists(i, j) && !hasBeenVisited(i,j);
    }
    private void traversePaths(int i,int j){
        //BASE CASE: if reached (n - 1, n - 1), count as path and stop.
        if (i == (n - 1) && j == (n - 1)) {
          this.numPaths++;
          return;
        }
        this.togglePiece(i, j);
        //RECURSIVE CASE: if next point is a viable position, go there and make the same decision

        //go right if possible
        if (this.viablePosition(i, j + 1)) {
          traversePaths(i, j + 1);
        }
      //go left if possible
        if (this.viablePosition(i, j - 1)) {
          traversePaths( i, j - 1);
        }

        //go down if possible
        if (this.viablePosition(i + 1, j)) {
          traversePaths( i + 1, j);
        }

        //go up if possible
        if (this.viablePosition(i - 1, j)) {
          traversePaths(i - 1, j);
        }

        //reset the board back to the way you found it after you've gone forward so that other paths can see it as a viable position for their routes
        this.togglePiece(i, j);

    }
    private int robotPaths(){

        traversePaths(0,0);
        return this.numPaths;
    }
    public static void main(String[] args) {
        paths_in_matrix mat=new paths_in_matrix();
        System.out.println(mat.robotPaths());
    }

}

如果您只需要计算有效路径:

假设您有一个矩阵n * m矩阵,并将所有像元设置为零,而"超限"像元设置为-1。

然后,您可以使用动态编程解决问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// a is a matrix with 0s and -1s
// n, m are the dimensions
// M is 10^9-7 incase you have a large matrix

if (a[0][0] == 0) a[0][0] = 1;
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        if (a[i][j] == -1) continue;
        if (i > 0) a[i][j] = (a[i][j] + max(a[i-1][j], 0LL)) % M;
        if (j > 0) a[i][j] = (a[i][j] + max(a[i][j-1], 0LL)) % M;
    }
}

// answer at lower right corner
cout << a[n-1][m-1];

快速燃烧,而无需递归或庞大的数据结构。

注意:由于重复而被删除,但是由于这是该主题的最佳话题,因此我从其他地方删除了答案,并将其添加到此处。


这是一个适用于矩形和正方形网格的完整实现。我将让您在每个路径的末尾弄清楚如何处理多余的" =>"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
   import java.util.Arraylist;

   public class PrintPath
   {
    static ArrayList<String> paths = new ArrayList<String>();

    public static long getUnique(int m, int n, int i, int j, String pathlist)
    {

        pathlist += ("(" + i +"," + (j) +") =>");

        if(m == i && n == j)
        {      
            paths.add(pathlist);
        }

        if( i > m || j > n)
        {
            return 0;              
        }

        return getUnique(m, n, i+1, j, pathlist)+getUnique(m, n, i, j+1, pathlist);

    }

    public static void printPaths()
    {
        int count = 1;
        System.out.println("There are"+paths.size() +" unique paths: \
"
);

        for (int i = paths.size()-1; i>=0; i--)
        {

         System.out.println("path" + count +":  " + paths.get(i));
         count++;
        }

    }

    public static void main(String args[])
    {
        final int start_Point = 1;
        int grid_Height = 2;
        int grid_Width = 2;

        getUnique(grid_Height, grid_Width, start_Point, start_Point,"");
        printPaths();

    }

}

这是c#版本(仅供参考),用于查找唯一路径(请注意,这里是使用动态编程返回路径数的版本(存储-懒惰)-计算从左上角到右下角的移动数以及在任何方向上的移动) (您可以参考我的博客以了解更多详细信息:http://codingworkout.blogspot.com/2014/08/robot-in-grid-unique-paths.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Tuple<int, int>[][] GetUniquePaths(int N)
        {
            var r = this.GetUniquePaths(1, 1, N);
            return r;
        }
        private Tuple<int, int>[][] GetUniquePaths(int row, int column, int N)
        {
            if ((row == N) && (column == N))
            {
                var r = new Tuple<int, int>[1][];
                r[0] = new Tuple<int, int>[] { new Tuple<int,int>(row, column) };
                return r;
            }
            if ((row > N) || (column > N))
            {
                return new Tuple<int, int>[0][];
            }
            var uniquePathsByMovingDown = this.GetUniquePaths(row + 1, column, N);
            var uniquePathsByMovingRight = this.GetUniquePaths(row, column + 1, N);
            List<Tuple<int, int>[]> paths = this.MergePaths(uniquePathsByMovingDown,
                row, column).ToList();
            paths.AddRange(this.MergePaths(uniquePathsByMovingRight, row, column));
            return paths.ToArray();
        }

哪里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private Tuple<int, int>[][] MergePaths(Tuple<int, int>[][] paths,
            int row, int column)
        {
            Tuple<int, int>[][] mergedPaths = new Tuple<int, int>[paths.Length][];
            if (paths.Length > 0)
            {
                Assert.IsTrue(paths.All(p => p.Length > 0));
                for (int i = 0; i < paths.Length; i++)
                {
                    List<Tuple<int, int>> mergedPath = new List<Tuple<int, int>>();
                    mergedPath.Add(new Tuple<int, int>(row, column));
                    mergedPath.AddRange(paths[i]);
                    mergedPaths[i] = mergedPath.ToArray();
                }
            }
            return mergedPaths;
        }

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[TestCategory(Constants.DynamicProgramming)]
        public void RobotInGridTests()
        {
            int p = this.GetNumberOfUniquePaths(3);
            Assert.AreEqual(p, 6);
            int p1 = this.GetUniquePaths_DP_Memoization_Lazy(3);
            Assert.AreEqual(p, p1);
            var p2 = this.GetUniquePaths(3);
            Assert.AreEqual(p1, p2.Length);
            foreach (var path in p2)
            {
                Debug.WriteLine("===================================================================");
                foreach (Tuple<int, int> t in path)
                {
                    Debug.Write(string.Format("({0}, {1}),", t.Item1, t.Item2));
                }
            }
            p = this.GetNumberOfUniquePaths(4);
            Assert.AreEqual(p, 20);
            p1 = this.GetUniquePaths_DP_Memoization_Lazy(4);
            Assert.AreEqual(p, p1);
            p2 = this.GetUniquePaths(4);
            Assert.AreEqual(p1, p2.Length);
            foreach (var path in p2)
            {
                Debug.WriteLine("===================================================================");
                foreach (Tuple<int, int> t in path)
                {
                    Debug.Write(string.Format("({0}, {1}),", t.Item1, t.Item2));
                }
            }
        }

场景:
1.想象有NxN个零索引矩阵。
2.机器人的初始位置是左上角,即(N-1,N-1)
3.机器人想到达右下角,即(0,0)

解:
-在任何可能的解决方案中,机器人都会将N个权利步骤和N个向下步骤移动到(0,0),或者可以说,初始机器人有权移动N个权利步骤和N个向下步骤。
-当机器人向右移动时,我们会将其剩余的右脚步数减少1,这与向下移动相同。
-在每个位置(边界处只有一个选项的机器人除外),机器人都有两个选项,一个是可以下降的,另一个是可以右移的。
-当机器人没有剩余的正确步骤时,它将终止。

**以下代码也具有驱动程序方法main(),您可以更改N的值。N可以> = 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class RobotPaths {

public static int robotPaths(int down, int right, String path)
{
    path = path+ down +","+ right +" ";
    if(down==0 && right==0)
    {
        System.out.println(path);
        return 1;
    }

    int counter = 0;

    if(down==0)
        counter = robotPaths(down, right-1, path);
    else if(right==0)
        counter = robotPaths(down-1, right, path);
    else
        counter = robotPaths(down, right-1, path) + robotPaths(down-1, right, path);

    return counter;
}

public static void main(String[] args)
{
    int N = 1;
    System.out.println("Total possible paths:"+RobotPaths.robotPaths(N-1, N-1,""));

}

}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int N;
function num_paths(intx,int y)
{
    int[][] arr = new int[N][N];
arr[N-1][N-1] = 0;
for(int i =0;i<N;i++)
{
    arr[N-1][i]=1;
    arr[i][N-1]=1;
}
for(int i = N-2;i>=0;i--)
{
    for(int j=N-2;j>=0;j--)
    {
        arr[i][j]= arr[i+1][j]+arr[i][j+1];
    }
}
return arr[0][0];
 }