我刚刚开始学习c#,但是我对Java有一定的经验,所以我想做的一件事就是生成一个2d数组映射,其中填充了1和0,然后递归检查是否存在不踩1的路径。
但是在编写代码并运行它之后,它向我显示了堆栈溢出异常错误。
我在哪里做错了什么?
编辑:数组的结尾(在这种情况下,最大值为9,9(10x10数组)的单元格设置为6)
递归方法的代码:
public static int recursiveCheck(int[,] grid,int x, int y, int finalX, int finalY, int paths)
{
if (grid[x, y] == 6)
{
paths++;
return paths;
}
if ((grid[x + 1, y] != 1) && (x+1 < finalX))
return recursiveCheck(grid, x + 1, y, finalX, finalY, paths);
if ((grid[x, y+1] != 1) && (y+1 < finalY))
return recursiveCheck(grid, x, y+1, finalX, finalY, paths);
if ((grid[x - 1, y] != 1) && (x > 0))
return recursiveCheck(grid, x - 1, y, finalX, finalY, paths);
if (grid[x, y - 1] != 1 && y >0)
return recursiveCheck(grid, x, y - 1, finalX, finalY, paths);
return 0;
}
数组初始化代码:
public static int[,] gridGen()
{
int[,] grid = new int[10, 10];
Random rnd = new Random();
int lim;
for (int i = 0; i < 10; i++)
{
for (int c = 0; c < 10; c++)
{
lim = rnd.Next(0, 2);
//Console.WriteLine($"lim: {lim} ");
grid[i, c] = lim;
}
}
grid[0, 0] = 2;
grid[grid.GetLength(0) - 1, grid.GetLength(1) - 1] = 6;
for (int i = 0; i < 10; i++)
{
for (int c = 0; c < 10; c++)
{
Console.Write(grid[i, c] + " ");
}
Console.WriteLine();
}
return grid;
}
答案 0 :(得分:0)
您无法跟踪已经访问过的空间。因此,假设您的网格具有彼此相邻的两个零,则您的函数将在递归的第一级移至一个,然后在第二级移至另一个,然后在第三级移回到第一个(即使第一级也是如此)递归仍在堆栈中),以此类推。
您可以通过多种方式来跟踪已访问过的地方。如果您希望在调用此方法时临时修改输入数组,则可以在查看周围的空间之前将当前的空间设置为1
。
public static int recursiveCheck(int[,] grid,int x, int y, int finalX, int finalY, int paths)
{
var originalValue = grid[x, y];
if (originalValue == 6)
{
paths++;
return paths;
}
grid[x, y] = 1; // prevent deeper recursion from coming back to this space
if ((grid[x + 1, y] != 1) && (x+1 < finalX))
return recursiveCheck(grid, x + 1, y, finalX, finalY, paths);
if ((grid[x, y+1] != 1) && (y+1 < finalY))
return recursiveCheck(grid, x, y+1, finalX, finalY, paths);
if ((grid[x - 1, y] != 1) && (x > 0))
return recursiveCheck(grid, x - 1, y, finalX, finalY, paths);
if (grid[x, y - 1] != 1 && y >0)
return recursiveCheck(grid, x, y - 1, finalX, finalY, paths);
grid[x, y] = originalValue; // allow other path checking to revisit this space.
return 0;
}
还请注意,您可以通过稍微递归递归来稍微简化一下事情:立即将边界检查和墙检查作为退出标准,并仅将路径数用作返回值,而不是输入。
public static int recursiveCheck(int[,] grid, int x, int y, int finalX, int finalY)
{
if(x < 0 || x > finalX || y < 0 || y > finalY)
{
return 0;
}
var originalValue = grid[x, y];
if (originalValue == 1)
{
return 0;
}
if (originalValue == 6)
{
return 1;
}
grid[x, y] = 1; // prevent deeper recursion from coming back to this space
var paths = recursiveCheck(grid, x + 1, y, finalX, finalY)
+ recursiveCheck(grid, x, y + 1, finalX, finalY)
+ recursiveCheck(grid, x - 1, y, finalX, finalY)
+ recursiveCheck(grid, x, y - 1, finalX, finalY);
grid[x, y] = originalValue; // allow other path checking to revisit this space.
return paths;
}
答案 1 :(得分:0)
这是另一种方式。
从返回网格中特定位置的有效邻居的函数开始:
private IEnumerable<(int r, int c)> GetNeighbours(int[,] grid, (int r, int c) reference)
{
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
{
var neighbour = (r: reference.r + i, c: reference.c + j);
if ((i != 0 && j != 0)
|| neighbour == reference
|| neighbour.r < 0 || neighbour.r >= grid.GetLength(0)
|| neighbour.c < 0 || neighbour.c >= grid.GetLength(1))
{
continue;
}
yield return neighbour;
}
}
这将返回左,右,上和下邻居,只要它在有效网格中即可。
然后,我们将创建一个public
方法来触发构建路径:
public List<(int r, int c)> RecursiveCheck(int[,] grid, (int r, int c) initial, int goal)
{
var path = new List<(int r, int c)>(new[] { initial });
return RecursiveCheck(grid, path, goal);
}
其目的是以initial
为起点,并将其变成一个元素的path
,然后调用真正的递归函数。
这里是:
private List<(int r, int c)> RecursiveCheck(int[,] grid, List<(int r, int c)> path, int goal)
{
foreach (var neighbour in GetNeighbours(grid, path.Last()))
{
if (!path.Contains(neighbour))
{
var next = path.Concat(new[] { neighbour }).ToList();
if (grid[neighbour.r, neighbour.c] == goal)
{
return next;
}
else if (grid[neighbour.r, neighbour.c] == 0)
{
return RecursiveCheck(grid, next, goal);
}
}
}
return new List<(int r, int c)>();
}
这将采用现有路径,查找路径中最后一个元素的所有邻居。它检查是否尚未访问这些邻居,然后构建next
路径。然后,它检查是否命中goal
,如果命中,则返回路径。否则,如果当前单元格的值为0
,则它将递归以查找解决方案。如果找不到解决方案,则返回空路径。
这是一个示例用法:
int[,] grid = new int[3, 5]
{
{ 0, 1, 0, 0, 0},
{ 0, 1, 0, 1, 0},
{ 0, 0, 0, 1, 6},
};
List<(int r, int c)> path = RecursiveCheck(grid, (r: 0, c: 0), 6);
Console.WriteLine(path.Count);
这将输出11
(这是从左上角找到6
的正确长度)。