c# - 在 C# 中查找闭合形状

标签 c# algorithm

假设您有一个正方形网格,其中每个网格项都是“已填充”或“未填充”,是否有一个很好的算法可以检测网格中的任何“实心”形状?

下图显示了我正在尝试完成的示例。该图像显示了一个封闭的形状,然后是两个可能有孔的形状。另请注意,形状不一定是简单的正方形或矩形,但不应存在直接对角线。

Example image with one filled shape and two potentials

  • 红色方 block 表示“实心”方 block 。
  • 蓝色方 block 代表封闭形状的内部区域
  • 白色方 block 未填充

然后我的问题是 - 是否有一种算法(数学一窍不通的人可以遵循的东西!希望使用 C# 实现)可以检测此网格中的单个封闭形状(或多个形状,如果存在),包括返回形状内的实际正方形?

到目前为止,我漫无边际的想法只涉及使用线性洪水填充,但这似乎并不合适,例如,没有单一的源点,但必须扫描整个网格以寻找可填充的形状。

最佳答案

多亏了@Groo 的评论,我意识到我的起步有点正确,那就是使用洪水填充。但是,我正在考虑扫描形状内部以找到我出错的边界。 Groo 建议,外面应该总是有一个点,然后从那里进行扫描。

enter image description here

我使用一个简单的二维 bool 数组(以及一个相当容易阅读的复制粘贴填充算法)编写了一个非常基本的测试程序,这似乎运行良好。我将这个简单的代码作为答案发布,以防它能帮助像我一样困惑的其他人,并且可能会阻止添加另一个措辞不佳的“问题”。

  class Program
  {
    static void Main(string[] args)
    {
      bool[,] grid;
      int width;
      int height;

      Console.Title = "Floodfill Shape Test";

      /*
       * This is a simple test program to detect filled shapes by performing a flood fill
       * to convert all empty space to solid - unless the empty space is already surrounded
       * by solid cells
       *
       * In order to this to work, the assumption is made that the boundaries of the grid
       * can never be solid before the flood fill is executed
       */

      width = 12;
      height = 10;
      grid = new bool[width, height];

      // add a sample enclosed shape
      grid[1, 1] = true;
      grid[2, 1] = true;
      grid[3, 1] = true;
      grid[1, 2] = true;
      grid[3, 2] = true;
      grid[1, 3] = true;
      grid[2, 3] = true;
      grid[3, 3] = true;

      // another enclosed shape
      grid[7, 1] = true;
      grid[8, 1] = true;
      grid[9, 1] = true;
      grid[10, 1] = true;
      grid[7, 2] = true;
      grid[10, 2] = true;
      grid[7, 3] = true;
      grid[8, 3] = true;
      grid[10, 3] = true;
      grid[8, 4] = true;
      grid[10, 4] = true;
      grid[8, 5] = true;
      grid[9, 5] = true;
      grid[10, 5] = true;

      // this shape has a hole in it
      grid[1, 5] = true;
      grid[2, 5] = true;
      grid[3, 5] = true;
      grid[1, 6] = true;
      grid[3, 6] = true;
      grid[1, 7] = true;
      grid[3, 7] = true;

      // a line right down the middle for the edge case
      // Remember that the boundaries can never be filled
      // or this house of cards will fall
      for (int i = 1; i < height - 1; i++)
      {
        grid[5, i] = true;
      }

      // display the original grid
      PrintGrid(grid, width, height);

      // run a basic flood-fill algorithm to mark as "solid" anything not already surrounded by solid borders
      FloodFill(grid, width, height);

      // display the modified grid
      PrintGrid(grid, width, height);

      if (Debugger.IsAttached)
      {
        Console.WriteLine("(Press any key to exit)");
        Console.ReadKey(true);
      }
    }

    private static void PrintGrid(bool[,] grid, int width, int height)
    {
      // print out the results
      // # - solid
      // . - empty
      // X - disallowed
      for (int row = 0; row < height; row++)
      {
        for (int col = 0; col < width; col++)
        {
          char c;

          if (row == 0 || row == height - 1 || col == 0 || col == width - 1)
          {
            c = 'X';
          }
          else {
            c = grid[col, row] ? '#' : '.';
          }

          Console.Write(c);
        }

        Console.WriteLine();
      }

      Console.WriteLine();
    }

    static void FloodFill(bool[,] grid, int width, int height)
    {
      // Taken from http://rosettacode.org/wiki/Bitmap/Flood_fill#C.23
      Queue<Point> q = new Queue<Point>();
      q.Enqueue(Point.Empty);
      while (q.Count > 0)
      {
        Point n = q.Dequeue();
        if (grid[n.X, n.Y])
          continue;
        Point w = n, e = new Point(n.X + 1, n.Y);
        while ((w.X >= 0) && !grid[w.X, w.Y])
        {
          grid[w.X, w.Y] = true;

          if ((w.Y > 0) && !grid[w.X, w.Y - 1])
            q.Enqueue(new Point(w.X, w.Y - 1));
          if ((w.Y < height - 1) && !grid[w.X, w.Y + 1])
            q.Enqueue(new Point(w.X, w.Y + 1));
          w.X--;
        }
        while ((e.X <= width - 1) && !grid[e.X, e.Y])
        {
          grid[e.X, e.Y] = true;

          if ((e.Y > 0) && !grid[e.X, e.Y - 1])
            q.Enqueue(new Point(e.X, e.Y - 1));
          if ((e.Y < height - 1) && !grid[e.X, e.Y + 1])
            q.Enqueue(new Point(e.X, e.Y + 1));
          e.X++;
        }
      }
    }
  }

关于c# - 在 C# 中查找闭合形状,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34395410/

相关文章:

c# - 更新 ASP 页面中的进度条

c# - 为什么 StackTrace.GetFrames 不直接返回引用?

Java模型更新多个 View

java - 使用随机枢轴元素而不是中间元素代码进行快速排序

c# - 顶级异常没有捕获任何东西

c# - 如何从模拟对象返回 IQueryable<TEntity> 对象?

c# - 如何为嵌套集合编写 FluentAssertion,与顺序无关?

javascript - 如何根据给定的输入数字创建动态多维数组,以便每个数组都应推送到其嵌套数组?

algorithm - 时间复杂度和实验结果

ruby - 字符串数组中的所有公共(public)子序列