[英]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#
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.