Algorithm 在2D位数组中查找位的连续区域 问题

Algorithm 在2D位数组中查找位的连续区域 问题,algorithm,graph-algorithm,Algorithm,Graph Algorithm,我有一个位数组,它表示一个二维的“贴图”的“瓷砖”。此图提供了位数组中位的图形示例: 我需要确定数组中存在多少连续的位“区域”。在上面的示例中,有两个这样的连续“区域”,如下所示: 瓷砖必须直接位于瓷砖的N、S、E或W处,以被视为“连续”。对角线接触的瓷砖不计算在内 到目前为止我得到了什么 因为这些位数组可能会变得相对较大(几MB大小),所以我有意避免在算法中使用任何类型的递归 伪代码如下所示: LET S BE SOURCE DATA ARRAY LET C BE ARRAY OF IDE

我有一个位数组,它表示一个二维的“贴图”的“瓷砖”。此图提供了位数组中位的图形示例:

我需要确定数组中存在多少连续的位“区域”。在上面的示例中,有两个这样的连续“区域”,如下所示:

瓷砖必须直接位于瓷砖的N、S、E或W处,以被视为“连续”。对角线接触的瓷砖不计算在内

到目前为止我得到了什么 因为这些位数组可能会变得相对较大(几MB大小),所以我有意避免在算法中使用任何类型的递归

伪代码如下所示:

LET S BE SOURCE DATA ARRAY
LET C BE ARRAY OF IDENTICAL LENGTH TO SOURCE DATA USED TO TRACK "CHECKED" BITS
FOREACH INDEX I IN S
    IF C[I] THEN 
        CONTINUE 
    ELSE
        SET C[I]
        IF S[I] THEN
            EXTRACT_AREA(S, C, I)

EXTRACT_AREA(S, C, I):
    LET T BE TARGET DATA ARRAY FOR STORING BITS OF THE AREA WE'RE EXTRACTING
    LET F BE STACK OF TILES TO SEARCH NEXT
    PUSH I UNTO F
    SET T[I]
    WHILE F IS NOT EMPTY
        LET X = POP FROM F
        IF C[X] THEN 
            CONTINUE
        ELSE
            SET C[X]
            IF S[X] THEN
                PUSH TILE NORTH OF X TO F
                PUSH TILE SOUTH OF X TO F
                PUSH TILE WEST OF X TO F
                PUSH TILE EAST OF X TO F
                SET T[X]
    RETURN T

我不喜欢我的解决方案的哪些方面
  • 只是为了运行,它需要两倍于它正在处理的位图数组的内存
  • 在提取“区域”时,它使用的内存是位图数组的三倍
  • 重复存在于“要检查的瓷砖”堆栈中-这看起来很难看,但不值得像我现在这样避免
我想看什么
  • 更好的内存配置文件
  • 更快速地处理大面积区域

解决方案/后续行动 我重新编写了只探索边缘的解决方案(按照@hatchet的建议)

这是非常简单的实现-并消除了需要保持“访问瓷砖”完全跟踪

基于三条简单规则,我可以遍历边,跟踪最小/最大x&y值,并在再次到达起点时完成

下面是我使用的三条规则的演示:


在一次采访中,我真的遇到了这样一个问题

您可以假设数组是一个图,连接的节点是相邻的节点。我的算法是向右移动1,直到找到一个标记的节点。当您找到一个时,请执行广度优先搜索,该搜索以O(n)为单位运行,并避免递归。当BFS返回时,从您停止的位置继续搜索,如果节点已被先前的BFS之一标记,则显然不需要搜索。我不确定你是否真的想返回找到的对象的数量,但是当你点击第一个标记的方块时,只需增加一个计数器就可以很容易地跟踪

通常,当您执行洪水填充类型算法时,会将您放置在一个位置并要求填充。由于这是查找所有填充区域的一种方法,您希望对其进行优化,这是为了避免重新检查以前BFS中已标记的节点,不幸的是,目前我想不出一种方法来执行此操作

减少内存消耗的一种黑客方法是太多地存储
short[][]
而不是布尔值。然后使用此方案避免生成整个第二个2d阵列

未标记=0,已标记=1,已检查和未标记=3,已检查和已标记=3


通过这种方式,您可以通过其值检查条目的状态,并避免生成第二个数组。

一种方法是周界漫游。 给定沿形状边缘的任意起始点,请记住该点

将边界框作为该点开始

使用顺时针规则集行走周界-如果用于到达当前点的点位于上方,则首先向右看,然后向下看,然后向左看,以找到形状周界上的下一个点。这有点像解决迷宫的简单策略,你不断地沿着墙走,总是向右走

每次访问新的周界点时,如果新的点在其外部,请展开边界框(即跟踪最小值和最大值x和y)

继续,直到到达起点

缺点:如果形状有很多单像素的“细丝”,你会在步行回来的时候再次看到它们

优点:如果形状有很大的内部占用空间,您就不必像在整体填充中记录访问的像素那样访问或记录它们

因此,它可以节省空间,但在某些情况下会以牺牲时间为代价

编辑

通常情况下,这个问题是已知的、命名的,并且有多个算法解决方案。您描述的问题称为最小边界矩形。解决这个问题的一种方法是使用。我上面描述的方法在该类中,称为or。我为它们提供的链接详细讨论了它们,并指出了我没有遇到的问题在你横过整个周界之前,有时你会重新开始起点。如果你的起点是沿着一个单一的像素“灯丝”的某个地方,在你完成之前你会重新访问它,除非你考虑到这种可能性,否则你会过早停止。NG问题。在该网站上的其他页面还讨论了另外两个算法:方块跟踪和Theo Pavlidis算法。需要注意的是,这些将对角线看作是相邻的,而不是这样的,但是这应该只是一些可以对基本算法进行少量修改的处理。 解决您的问题的另一种方法是。不过,对于您的需要,这可能是一种比您需要的时间更昂贵的解决方案

额外资源:


你的算法的运行时间是什么?还有一个问题,算法的最终结果是什么,它不是细节和图片的整体填充正确+1。Offtopic,你是如何创建第三幅图像的?进行整体填充的最佳方法是广度优先搜索,以避免recursion@Steve-如果你只想要封闭的矩形,你能沿着周界走,直到你回到起点,边走边记录最小值和最大值x和y。这只需要很少的额外空间。@Steve谢谢,我认为运行时仍然是O(n),我希望我能想出一种方法,完全跳过我已经标记为对象的部分,并改进运行时。是的,我在查看代码以尝试确定运行时,但我