为什么这种洪水填充算法会导致堆栈溢出?

发布于 2024-08-17 18:52:37 字数 611 浏览 3 评论 0原文

void FloodFill(int layer, int x, int y, int target, int replacement)
{
    if (x < 0) return;
    if (y < 0) return;
    if (x >= _mapWidth) return;
    if (y >= _mapHeight) return;

    if (_mapLayers[layer, x, y] != target) return;

    _mapLayers[layer, x, y] = replacement;

    FloodFill(layer, x - 1, y, target, replacement);
    FloodFill(layer, x + 1, y, target, replacement);
    FloodFill(layer, x, y - 1, target, replacement);
    FloodFill(layer, x, y + 1, target, replacement);

    return;
}

到目前为止,这是我的代码,但是当它到达地图末尾时,它会导致堆栈溢出,有人知道如何解决这个问题(可能是一个棘手的情况)吗?

void FloodFill(int layer, int x, int y, int target, int replacement)
{
    if (x < 0) return;
    if (y < 0) return;
    if (x >= _mapWidth) return;
    if (y >= _mapHeight) return;

    if (_mapLayers[layer, x, y] != target) return;

    _mapLayers[layer, x, y] = replacement;

    FloodFill(layer, x - 1, y, target, replacement);
    FloodFill(layer, x + 1, y, target, replacement);
    FloodFill(layer, x, y - 1, target, replacement);
    FloodFill(layer, x, y + 1, target, replacement);

    return;
}

This is my code so far, but when it reaches the end of the map it causes a Stack Overflow, does anybody know how to solve the problem (probably a tricky condition)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

兔姬 2024-08-24 18:52:37

请注意,存在此调用路径:

(x, y) -> (x+1, y) -> (x+1-1, y) -> (x+1-1+1, y) -> ...

这是无限递归,因此会出现堆栈溢出。你的支票无法解决这个问题。您必须执行一项额外检查:

if (_mapLayers[layer, x, y] == replacement) return;

即使您已包含上面的额外检查,请注意最大递归深度为 (_mapWidth * _mapHeight),即使对于小位图,该深度也可能非常深(例如100×100)。

Notice that this call path exists:

(x, y) -> (x+1, y) -> (x+1-1, y) -> (x+1-1+1, y) -> ...

This is an infinite recursion, so you have stack overflow. Your checks can't deal with this. You have to perform one extra check:

if (_mapLayers[layer, x, y] == replacement) return;

Even if you have included the extra check above, note that the maximum recursion depth is (_mapWidth * _mapHeight), which can be very deep even for a small bitmap (e.g. 100 x 100).

漫漫岁月 2024-08-24 18:52:37

首先,您应该确保 target!=replacement (可以在初始调用“FloodFill”之前完成一次)。然后,只要 _mapWidth 和 _mapHeight 不是特别大(这在很大程度上取决于 _mapLayers 数组的内容),您的算法就可以工作。如果这是一个问题,您应该尝试非递归算法。创建 a

class Point
{ 
    public int x, y;
    public Point(int newX, int newY)
    {
         x=newX;
         y=newY;
    }
}

和 a

 List<Point> pointList;

将初始点放入此列表中并运行某种循环,直到 pointList 为空:从列表中取出一个点,像上面一样处理它,而不是上面的原始递归调用,将四个邻居再次放入列表中。

编辑:这是完整的示例,但没有测试它:

    void FloodFill2(int layer, int xStart, int yStart, int target, int replacement)
    {
        if(target==replacement)
            return;
        List<Point> pointList = new List<Point>();

        pointList.Add(new Point(xStart,yStart));
        while(pointList.Count>0)
        {
            Point p = pointList[pointList.Count-1];
            pointList.RemoveAt(pointList.Count-1);
            if (p.x < 0) continue;
            if (p.y < 0) continue;
            if (p.x >= _mapWidth) continue;
            if (p.y >= _mapHeight) continue;
            if (_mapLayers[layer, p.x, p.y] != target) continue;
            _mapLayers[layer, p.x, p.y] = replacement;

            pointList.Add(new Point(p.x - 1, p.y));
            pointList.Add(new Point(p.x + 1, p.y));
            pointList.Add(new Point(p.x, p.y - 1));
            pointList.Add(new Point(p.x, p.y + 1));
        }
    }

编辑2:事实上,这里有一个优化例程的建议:如果插入毫无意义,请避免插入到列表中,所以:

            if(p.x>=0) 
                 pointList.Add(new Point(p.x - 1, p.y));
            if(p.x<_mapWidth-1) 
                 pointList.Add(new Point(p.x + 1, p.y));
            if(p.y>=0) 
                 pointList.Add(new Point(p.x, p.y - 1));
            if(p.y<_mapHeight-1) 
                 pointList.Add(new Point(p.x, p.y + 1));

First of all, you should make sure that target!=replacement (can be done once before the inital call of 'FloodFill'). Then, your algo may work, as long as _mapWidth and _mapHeight are not extraordinary large (it depends heavily on the content of your _mapLayers array). If this is a problem, you should try a non-recursive algorithm. Create a

class Point
{ 
    public int x, y;
    public Point(int newX, int newY)
    {
         x=newX;
         y=newY;
    }
}

and a

 List<Point> pointList;

Put the initial point into this list and run some kind of loop until pointList is empty: take one point out of the list, process it like above and instead of the original recursive call above put the four neighbours again into the list.

EDIT: here is the complete example, did not test it, though:

    void FloodFill2(int layer, int xStart, int yStart, int target, int replacement)
    {
        if(target==replacement)
            return;
        List<Point> pointList = new List<Point>();

        pointList.Add(new Point(xStart,yStart));
        while(pointList.Count>0)
        {
            Point p = pointList[pointList.Count-1];
            pointList.RemoveAt(pointList.Count-1);
            if (p.x < 0) continue;
            if (p.y < 0) continue;
            if (p.x >= _mapWidth) continue;
            if (p.y >= _mapHeight) continue;
            if (_mapLayers[layer, p.x, p.y] != target) continue;
            _mapLayers[layer, p.x, p.y] = replacement;

            pointList.Add(new Point(p.x - 1, p.y));
            pointList.Add(new Point(p.x + 1, p.y));
            pointList.Add(new Point(p.x, p.y - 1));
            pointList.Add(new Point(p.x, p.y + 1));
        }
    }

EDIT2: In fact, here is a suggestion to optimize the routine: avoid inserting to the list if inserting gets pointless, so:

            if(p.x>=0) 
                 pointList.Add(new Point(p.x - 1, p.y));
            if(p.x<_mapWidth-1) 
                 pointList.Add(new Point(p.x + 1, p.y));
            if(p.y>=0) 
                 pointList.Add(new Point(p.x, p.y - 1));
            if(p.y<_mapHeight-1) 
                 pointList.Add(new Point(p.x, p.y + 1));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文