簡體   English   中英

在 c# 中查找 map 區域的算法

[英]Algorithm to find map regions in c#

我想給這些區域編號。我在下面分享我缺少的算法。

int i = 1;

            for (int x= 0; x< height ; x++)
            {
                for (int y= 0; y< width ; y++)
                {
                    if (y>= 1)
                    {
                        if (a[x- 1, ] != "0")
                        {
                            if(a[x, y]!="0")
                            {
                                a[x, y] = a[x- 1, y];
                            }
                        }
                        else
                        {
                            if(a[x, y] != "0")
                            {
                                a[x, y] = "?";
                            }
                        }
                    }
                    else
                    {
                        if (a[x, y] != "0")
                        {
                            a[x, y] = i.ToString();
                        }
                        else
                        {
                            if (x>= 1)
                            {
                                if (a[x, y- 1] != a[x, y])
                                    i++;
                            }
                        }
                    }
                    Console.Write(string.Format("{0}  ", a[x, y]));
                }
                Console.Write(Environment.NewLine);
            }

您對更有效的算法有什么建議或任何想法如何改進此算法。謝謝您的幫助。

一個簡單但有效的幼稚實現是這樣做:

  1. 查找任何未編號的單元格(任何算法都適合,簡單的線性搜索是天真的實現)
  2. 使用洪水填充算法來填充這個單元格所在的區域,用一個新的數字
  3. 沖洗並重復,直到第 1 步找不到更多未編號的單元格

這是一個簡單的實現。 請注意,我使用int?[,]矩形數組來保存 map,您必須根據需要調整代碼。 I use LINQPad to test this in, so the .Dump() extension method there simply dumps the output of the LINQ query to the output window. 示例 output 下面的代碼:

void Main()
{
    var map = new int?[30, 20];

    for (int x = 0; x < 30; x++)
    {
        map[x, 10 - x / 3] = 0;
        map[x, 5 + x / 2] = 0;
        map[x, 18 - x / 5] = 0;
    }
    ShowMap(map);
    NumberRegions(map);
    ShowMap(map);
}

public static void ShowMap(int?[,] map)
{
    Enumerable.Range(0, map.GetLength(1))
        .Select(y => string.Join("", Enumerable.Range(0, map.GetLength(0)).Select(x => map[x, y]?.ToString() ?? "?")))
        .Dump();
}

public static void NumberRegions(int?[,] map)
{
    var stack = new Stack<(int x, int y)>();
    int nextNumber = 1;

    for (int x = 0; x < map.GetLength(0); x++)
    {
        for (int y = 0; y < map.GetLength(1); y++)
        {
            if (map[x, y] is null)
            {
                stack.Push((x, y));
                while (stack.Any())
                {
                    (int cx, int cy) = stack.Pop();
                    if (map[cx, cy] is null)
                    {
                        map[cx, cy] = nextNumber;
                        if (cx > 0) stack.Push((cx - 1, cy));
                        if (cx < map.GetLength(0) - 1) stack.Push((cx + 1, cy));
                        if (cy > 0) stack.Push((cx, cy - 1));
                        if (cy < map.GetLength(1) - 1) stack.Push((cx, cy + 1));
                    }
                }

                nextNumber++;
            }
        }
    }
}

這是示例 output:

Before:
?????????????????????????????? 
???????????????????????????000 
????????????????????????000??? 
?????????????????????000?????? 
??????????????????000????????? 
00?????????????000???????????? 
??00????????000??????????????? 
????00???000?????????????????? 
??????000????????????????????? 
???000??00???????????????????? 
000???????00?????????????????? 
????????????00???????????????? 
??????????????00?????????????? 
????????????????00???????00000 
??????????????????0000000????? 
???????????????0000000???????? 
??????????00000???????00?????? 
?????00000??????????????00???? 
00000?????????????????????00?? 
????????????????????????????00 

After:
111111111111111111111111111111 
111111111111111111111111111000 
111111111111111111111111000555 
111111111111111111111000555555 
111111111111111111000555555555 
001111111111111000555555555555 
220011111111000555555555555555 
222200111000555555555555555555 
222222000555555555555555555555 
222000330055555555555555555555 
000333333300555555555555555555 
333333333333005555555555555555 
333333333333330055555555555555 
333333333333333300555555500000 
333333333333333333000000066666 
333333333333333000000066666666 
333333333300000444444400666666 
333330000044444444444444006666 
000004444444444444444444440066 
444444444444444444444444444400 

我說這是一個幼稚的實現的原因是,如果你有一個非常大的 map,“搜索下一個未編號的單元格”可能很重要。 如果你能找到一個好方法來跟蹤你發現的 0 邊界,以便你可以在這些邊界周圍尋找未編號的單元格,那可能會奏效,但這里有很多邊緣情況需要考慮,所以我什至沒有打擾試。


這是一個變體,它簡單地將洪水填充實現拆分為一個單獨的方法:

public static void NumberRegions(int?[,] map)
{
    int nextNumber = 1;
    for (int x = 0; x < map.GetLength(0); x++)
        for (int y = 0; y < map.GetLength(1); y++)
            if (map[x, y] is null)
            {
                FloodFill(map, x, y, nextNumber);
                nextNumber++;
            }
}

public static void FloodFill(int?[,] map, int x, int y, int number)
{
    var work = new Stack<(int x, int y)>();
    work.Push((x, y));
    while (work.Any())
    {
        (x, y) = work.Pop();
        if (map[x, y] is not null)
            continue;
            
        map[x, y] = number;
        if (x > 0) work.Push((x - 1, y));
        if (x < map.GetLength(0) - 1) work.Push((x + 1, y));
        if (y > 0) work.Push((x, y - 1));
        if (y < map.GetLength(1) - 1) work.Push((x, y + 1));
    }
}

由於您在評論中提到了一些要求,如果您不能使用Stack<>類型,這里是上面 FloodFill 方法的遞歸版本:

public static void FloodFill(int?[,] map, int x, int y, int number)
{
    if (map[x, y] is null)
    {
        map[x, y] = number;
        if (x > 0) FloodFill(map, x-1, y, number);
        if (x < map.GetLength(0) - 1) FloodFill(map, x+1, y, number);
        if (y > 0) FloodFill(map, x, y-1, number);
        if (y < map.GetLength(1) - 1) FloodFill(map, x, y+1, number);
    }
}

暫無
暫無

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

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