递归函数中的堆栈溢出异常错误

时间:2019-05-09 02:51:10

标签: c#

我刚刚开始学习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;
        }

2 个答案:

答案 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的正确长度)。