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

时间:2010-01-17 14:22:07

标签: c# algorithm map tiles

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;
}

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

2 个答案:

答案 0 :(得分:3)

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

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

这是一个无限递归,所以你有堆栈溢出。你的支票无法解决这个问题。你必须进行一次额外的检查:

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

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

答案 1 :(得分:2)

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

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

 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));
        }
    }

EDIT2:事实上,这里有一个优化例程的建议:如果插入没有意义,请避免插入列表,所以:

            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));