簡體   English   中英

洪水填充算法stackoverflow

[英]Flood fill algorithm stackoverflow

我正在做一個繪畫游戲,但我無法讓洪水填充算法在大面積上工作。 它是一種遞歸算法,我讀到實現堆棧可能有效,但我也可以讓它工作。 這是代碼;

    private void FillCluster(int x, int y, int colorIndex, HashSet<string> traversedCells)
        {
            Debug.Log(colorIndex);
            // Check if this cell is within the bounds of the picture and has a color number and the
            if(x < 0 || x >= ActivePictureInfo.XCells ||
                y < 0 || y >= ActivePictureInfo.YCells ||
                ActivePictureInfo.ColorNumbers[y][x] == -1 ||
                ActivePictureInfo.ColorNumbers[y][x] != colorIndex)
            {
                return;
            }

            string cellKey = string.Format("{0}_{1}", x, y);

            // Check if this cell has already been traversed by FillBlob
            if (traversedCells.Contains(cellKey))
            {
                return;
            }
            
            // Check if this cell is already colored in with the correct color
            if (!ActivePictureInfo.HasProgress || ActivePictureInfo.Progress[y][x] != -1)
            {
                ColorCell(x, y, colorIndex);
            }
            
            // Add this cells key to the traversed hashset to indicate it has been processed
            traversedCells.Add(cellKey);
            
            // Recursively call recursively with the four cells adjacent to this cell
            FillCluster(x - 1, y, colorIndex, traversedCells);
            FillCluster(x + 1, y, colorIndex, traversedCells);
            FillCluster(x, y - 1, colorIndex, traversedCells);
            FillCluster(x, y + 1, colorIndex, traversedCells);
            FillCluster(x - 1, y - 1, colorIndex, traversedCells);
            FillCluster(x - 1, y + 1, colorIndex, traversedCells);
            FillCluster(x + 1, y - 1, colorIndex, traversedCells);
            FillCluster(x + 1, y + 1, colorIndex, traversedCells);
        }

所以你得到一個 stackOverflow 的原因是因為前一次迭代的狀態被保存在堆棧中,除非這兩個條件都滿足:

  • 你正在做尾遞歸(查找它,但它現在不相關)
  • 您的語言優化了尾遞歸(C# 沒有)

由於堆棧大小是有限的,並且您的算法很快就會一個接一個地達到數千次調用,因此您無法使遞歸版本在 C# 中工作,這是不可能的,但您似乎已經理解這一點

這是一個無需遞歸的偽代碼解決方案。 我不知道 C# 所以合成器是不正確的,但想法是正確的,你必須將它轉換成正確的 C#

private void FillCluster(int x, int y, int colorIndex, HashSet<string> traversedCells) {
    Debug.Log(colorIndex);
    // Check if this cell is within the bounds of the picture and has a color number and the
    
    //Declare a set, add inside the current node
    Set<Tuple> nodesToFill = new Set<>()
    nodesToFill.add(new Tuple(x, y));
    //Replace the recursion by a loop
    for (Tuple tuple in nodesToFill) {
        //initialize the current position
        x = tuple.first
        y = tuple.second
        //Deal with the current node
        if(FillClusterInner(x, y, colorIndex, traversedCells)) {
        //Add the new nodes to the set instead of using recursion
            nodesToFill.add(new Tuple(x-1, y))
            nodesToFill.add(new Tuple(x + 1, y))
            nodesToFill.add(new Tuple(x, y - 1))
            nodesToFill.add(new Tuple(x, y + 1))
            nodesToFill.add(new Tuple(x - 1, y - 1))
            nodesToFill.add(new Tuple(x - 1, y + 1))
            nodesToFill.add(new Tuple(x + 1, y - 1))
            nodesToFill.add(new Tuple(x + 1, y + 1))
        }
        //Remove the current tuple from the set as you have dealt with it
        nodesToFill.remove(tuple)
    }
}

//This is a non-recursive method which fills a single specified node
bool FillClusterInner(int x, int y, int colorIndex, HashSet<string> traversedCells) {
    if(x < 0 || x >= ActivePictureInfo.XCells ||
            y < 0 || y >= ActivePictureInfo.YCells ||
            ActivePictureInfo.ColorNumbers[y][x] == -1 ||
            ActivePictureInfo.ColorNumbers[y][x] != colorIndex)
    {
        return false;
    }
        
    string cellKey = string.Format("{0}_{1}", x, y);

    // Check if this cell has already been traversed by FillBlob
    if (traversedCells.Contains(cellKey))
    {
        return false;
    }
    
    // Check if this cell is already colored in with the correct color
    if (!ActivePictureInfo.HasProgress || ActivePictureInfo.Progress[y][x] != -1)
    {
        ColorCell(x, y, colorIndex);
    }
    
    // Add this cells key to the traversed hashset to indicate it has been processed
    traversedCells.Add(cellKey);
    return true;
}

您可以看到這個想法:我們使用集合代替遞歸,其中包含我們尚未填充的節點的所有索引。 您添加到這個集合而不是調用遞歸函數,並且從集合中刪除您處理的每個位置。 當集合為空時,您已填充了每個必須填充的單元格。

無需記住所有訪問過的單元格的版本,因為它們已經用顏色標記

    private void FillCluster(int x, int y, int colorIndex)
    {
        Debug.Log(colorIndex);

        var currentSeam = new Queue<PointDirection>();
        if (FillPoint(x, y, colorIndex))
        {
            currentSeam.Enqueue(new PointDirection(x - 1, y, Direction.Left));
            currentSeam.Enqueue(new PointDirection(x + 1, y, Direction.Right));
            currentSeam.Enqueue(new PointDirection(x, y - 1, Direction.Up));
            currentSeam.Enqueue(new PointDirection(x, y + 1, Direction.Down));
        }
        while (currentSeam.Count > 0)
        {
            var current = currentSeam.Dequeue();
            if (FillPoint(current.X, current.Y, colorIndex))
            {
                if (current.Direction != Direction.Right)
                    currentSeam.Enqueue(new PointDirection(x - 1, y, Direction.Left));
                if (current.Direction != Direction.Left)
                    currentSeam.Enqueue(new PointDirection(x + 1, y, Direction.Right));
                if (current.Direction != Direction.Down)
                    currentSeam.Enqueue(new PointDirection(x, y - 1, Direction.Up));
                if (current.Direction != Direction.Up)
                    currentSeam.Enqueue(new PointDirection(x, y + 1, Direction.Down));
            }
        }
    }

    private bool FillPoint(int x, int y, int colorIndex)
    {
        if (x < 0 || x >= ActivePictureInfo.XCells ||
            y < 0 || y >= ActivePictureInfo.YCells ||
            ActivePictureInfo.ColorNumbers[y][x] == -1 ||
            ActivePictureInfo.ColorNumbers[y][x] == colorIndex)
        {
            return false;
        }
        ActivePictureInfo.ColorNumbers[y][x] = colorIndex;
        return true;
    }

    private struct PointDirection
    {
        public PointDirection(int x, int y, Direction direction)
        {
            X = x;
            Y = y;
            Direction = direction;
        }

        public int X { get; }
        public int Y { get; }
        public Direction Direction { get; }
    }

    private enum Direction : byte
    {
        Up,
        Right,
        Down,
        Left
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM