通过仅检查“开放”索引列表来查找网格中所有最大的开放矩形的列表

时间:2013-08-29 16:28:17

标签: arrays algorithm actionscript

我有一个2D网格,里面有一些障碍。

[x][x][ ]
[ ][ ][ ]
[ ][ ][x]

我希望能够找到“开放”网格单元格的所有最大空间。

[x][x][ ]       [x][x][ ]
[1][1][ ]       [2][2][2]
[1][1][x]  and  [ ][ ][x]

我看到类似的问题here;但是,我可以访问的数据不是2D数组值。我有一个只有开放/可用索引的列表,它存储在一维数组中。使用上面的示例,我的数据如下所示:

[0,2]
[1,0]
[1,1]
[1,2]
[2,0]
[2,1]

我可以把它翻译成二维数组,但我不想再次遍历所有项目来做到这一点。我觉得有一些非常简单的解决方案,但我似乎无法找到它。

有没有办法只通过检查开放指数列表来找到所有最大的矩形?

修改 我刚决定将开放索引数组更改为代表整个网格的2D数组,然后使用我链接的算法。这对我的初始测试足够有效;但是,我有兴趣阅读其他可能的解决方案,我是否需要改变我的性能或空间问题的实现。

3 个答案:

答案 0 :(得分:0)

当然。

要查找矩形:

  • 从任何免费单元格开始。假设这将是左上角。
  • 查看它是否可以水平扩展。
  • 看看它是否可以垂直延伸。
  • 原路返回。

我稍后会对此进行详细说明,但我认为这可以让你起步。

答案 1 :(得分:0)

可用坐标列表已排序。

所以有一个全局的,现在最大的矩形,也是列表。

执行列表行走,并为每个项目调用具有从该项目开始的子列表的函数。要找到从该项目左上角开始的最大矩形。

public List<Coord> getMaxRectangle(List<Coord> openCoords) {
    List<Coord> currentMaxRectangle = null;
    for (int i = 0; i < openCoords.size(); ++i) {
        // Handle all rectangles starting here:
        currentMaxRectangle = maxRectangleStartingAt(i, currentMaxRectangle);
    }
    return currentMaxRectangle;
}

public List<Coord> maxRectangleStartingAt(int i, List<Coord> openCoords) {
    // Now the interesting part:
    // Take the maximal width list first: standing for all widths 1, ... max width.
    List<Coord> rect = new ArrayList<>();
    for (int j = i; j < openCoords.size()
            && openCoords.get(j).y == openCoords.get(i).y
            && openCoords.get(j).x - openCoords.get(i).x == j - i;
            ++j) {
        rect.add(openCoords.get(j);
    }
    int maxWidth = rect.size();
    // Now we can look for next lines below rect.get(0), ... get(maxWidth - 1),
    // continuously diminishing maxWidth till 0.
    // When we need to diminish maxWidth, we can look wether the original
    // maxWidth by (currentY - 1) is a maximum result.
    ...
}

所以我们走路:

           +-------------+ 1st rect
           |           |
           |           | 2nd rect
           |      | 3rd rect
           |   | 4th rect

O(N²)复杂性的演示:

public static void main(String[] args) {
    int[][] openCoords = {
        {0, 2},
        {1, 0},
        {1, 1},
        {1, 2},
        {2, 0},
        {2, 1}
    };
    new MaxRectFinder().find(openCoords);
}

private int[] maximumTopLeft;
private int[] maximumSize;

private void find(int[][] openCoords) {
    maximumTopLeft = null;
    maximumSize = new int[2];
    for (int topLeftCandidateI = 0; topLeftCandidateI < openCoords.length; ++topLeftCandidateI) {
        int yOrig = openCoords[topLeftCandidateI][0];
        int xOrig = openCoords[topLeftCandidateI][1];
        int y = yOrig;
        int x = xOrig + 1;
        int j = topLeftCandidateI + 1;
        while (j < openCoords.length
                && openCoords[j][0] == y
                && openCoords[j][1] == x) {
            ++x;
            ++j;
        }
        // Now we have a maximum x.
        for (;;) {
            // Skip after right side on current line:
            while (j < openCoords.length
                    && openCoords[j][0] == y) {
                ++j;
            }
            ++y;
            // Check for maximum:
            if (maximumTopLeft == null || (y - yOrig)*(x - xOrig) > maximumSize[0] * maximumSize[1]) {
                maximumSize[0] = y - yOrig;
                maximumSize[1] = x - xOrig;
                maximumTopLeft = openCoords[topLeftCandidateI];
            }
            // Skip before left side below origin:
            while (j < openCoords.length
                    && openCoords[j][0] == y
                    && openCoords[j][1] < x) {
                ++j;
            }
            if (j >= openCoords.length
                    || openCoords[j][0] != y
                    || openCoords[j][1] != x) {
                break;
            }
            // Register rectangle part:
            int x2 = xOrig;
            while (j < openCoords.length
                    && openCoords[j][0] == y
                    && openCoords[j][1] == x2
                    && x2 <= x) {
                ++x2;
                ++j;
            }
            x = x2;
        }
    }
    if (maximumTopLeft == null) {
        System.out.println("No solution found, not even 1x1.");
    } else {
        System.out.printf("At [%d, %d] with size [%d, %d].%n",
                maximumTopLeft[0], maximumTopLeft[1], maximumSize[0], maximumSize[1]);
    }
}

答案 2 :(得分:0)

我之前的回答是简单的方法。这完全不同。

算法

  1. 识别水平线并转换为矩形表示法。

  2. 使用行0中的矩形初始化currRects列表。

  3. 将currRow设置为1。

  4. 对于b中的每个矩形currRects,找到行a中水平线currRow的交叉点。交叉操作i定义为

        r(axi-axt,ayi-ayt) i r(bxi-bxt,byi-byt)
    

    它的相关性是矩形的y范围是如何相交的。以图形方式描述:

           bbb
        aa|   |     No intersect
        aa|aa |     Partial intersect
        aa|aaa|     a contains b
        aa|aaa|a    a contains b
          |aa |     b contains a
          |aaa|     a equal b
          |aaa|a    a contains b
          | aa|     b contains a
          | aa|a    Partial intersect
          |   |aa   No intersect
    

    确定每个案例的必要逻辑基于ayiaytbyibyt而且很简单,因此这里没有描述。

    此函数将返回最多2个矩形和一个标志的列表。

        No intersect       ([],true)
        Partial intersect: ([red,a],true)
        b contains a:      ([red],true)
        a contains b:      ([pres,a],false)
        a equal b:         ([pres],false)
    

    redpres是将前一个矩形b与传入的a组合在一起的结果。可以pres测量矩形的宽度(在y坐标的意义上),或red使用它,但差异在算法上并不重要。

    步骤4的核心是保持列表nextCurrRects包含函数intersect返回的所有矩形。当然,nextCurrRects应该是空的。此外,如果对于前一个b中的矩形currRects,返回的标记永远不会false,则需要将b放入最终结果列表中(或者只是打印或“产生”)。

  5. 使currRects等于nextCurrRects。如果currRow尚未到达,请进行迭代。

  6. currRects中的所有矩形也属于结果。

  7. 实施例

    1)inRects= [ r(0-0,2-2), r(1-1,0-2), r(2-2,0-1) ]

    2)currRects= [ r(0-0,2-2) ]

    3)currRow= 1

    4)

        nextCurrRects= []
        r(1-1,0-2) i r(0-0,2-2) {a contains b} == ( [ r(0-1,2-2), r(1-1,0-2)] ], false )
        nextCurrRects == [ r(0-1,2-2), r(1-1,0-2)] ]
    

    5)currRects == [ r(0-1,2-2), r(1-1,0-2)] ]

    4)

        nextCurrRects= []
    
        r(2-2,0-1) i r(0-1,2-2) {No intersect} == ( [], true )
        Never false -> result= [ r(0-1,2-2) ]
    
        r(2-2,0-1) i r(1-1,0-2) {Partial intersect} == ( r(1-2,0-1), true )
        nextCurrRects= [ r(1-2,0-1) ]
        Never false -> result= [ r(0-1,2-2) , r(1-2,0-1) ]
    

    5)currRects= [ r(1-2,0-1) ]

    6)result= [ r(0-1,2-2) , r(1-2,0-1) , r(1-2,0-1) ]

    事实上,该算法只是“发现”在原始示例中未考虑第3个结果矩形:

        [x][x][3]
        [ ][ ][3]
        [ ][ ][x]
    

    代码

    class Range:
        def __init__(self,start,end=None):
            self.start= start
            self.end= end if end is not None else start
        def isEmpty(self):
            return self.start > self.end
        def isUnit(self):
            return self.start == self.end
        def intersect(self,other):
            return Range( max(self.start,other.start) , min(self.end,other.end) )
        def contains(self,other):
            return self.start <= other.start and self.end >= other.end 
        def __repr__(self):
            return "Range(%d,%d)" % ( self.start, self.end )
    
    class Rect:
        def __init__(self,xRange,yRange):
            self.xRange= xRange
            self.yRange= yRange
        def isEmpty(self):
            return self.xRange.isEmpty() or self.yRange.isEmpty()
        def isUnit(self):
            return self.xRange.isUnit() and self.yRange.isUnit()
        def intersect(self,other):
            return Range( max(self.start,other.start) , min(self.end,other.end) )
        def contains(self,other):
            return self.xRange.contains(other.xRange) and self.yRange.contains(other.yRange)
        def __repr__(self):
            return "Rect(%s,%s)" % ( self.xRange, self.yRange )
    
    def intersect(a,b):
        r= Rect( Range(b.xRange.start,a.xRange.end) , a.yRange.intersect(b.yRange) )
        brokenB= not a.yRange.contains(b.yRange)
        fullyAbsorbedA= b.yRange.contains(a.yRange)
        return (r,brokenB,fullyAbsorbedA)
    
    def findOpenRectangles(freeElements,pastRowNum):        
    
        #  From `freeElements`, compute free runs into `freeRunsPerRow`
        from collections import defaultdict
        freeRunsPerRow= defaultdict(set)
        rowNum= -1
        currRun= None
        for fe in freeElements :
            if fe[0] != rowNum :
                if currRun is not None:
                    freeRunsPerRow[rowNum]|= { Rect(Range(rowNum),currRun) }
                currRun= Range(fe[1],fe[1])
                rowNum= fe[0]
            elif fe[1] == currRun.end + 1 :
                currRun= Range(currRun.start,fe[1])
            else:
                freeRunsPerRow[rowNum]|= { Rect(Range(rowNum),currRun) }
                currRun= Range(fe[1],fe[1])
        if currRun is not None:
            freeRunsPerRow[rowNum]|= { Rect(Range(rowNum),currRun) }
            currRun= None
        for freeRuns in freeRunsPerRow.items() :
            print( freeRuns )
    
        #  Yield open rectangles
        currRects= set()
        for currRow in range(0,pastRowNum) :
            continuingRects= set()
            startingRects= set( freeRunsPerRow[currRow] )
            for b in currRects :
                brokenB= True
                for a in freeRunsPerRow[currRow] :
                    modifiedContinuingRect, t_brokenB, t_absorbedA= intersect(a,b)
                    if not modifiedContinuingRect.isEmpty() and not [ x for x in continuingRects if x.contains(modifiedContinuingRect) ] :
                        continuingRects-= { x for x in continuingRects if modifiedContinuingRect.contains(x) }
                        continuingRects|= {modifiedContinuingRect}
                    if not t_brokenB : brokenB= False
                    if t_absorbedA : startingRects-= {a}
                if brokenB and not b.isUnit() : yield b
            currRects= continuingRects
            currRects|= startingRects
        for b in currRects : 
            if not b.isUnit() : yield b
    

    测试/实施例

    使用以下测试代码:

    input= []
    input.append("   X    ")
    input.append("        ")
    input.append(" X      ")
    input.append("     X X")
    input.append("        ")
    input.append("        ")
    input.append("    X   ")
    input.append("   X  X ")
    input.append(" X     X")
    
    # Translates input into a list of coordinates of free elements
    freeElements= []
    for rowNum, row in enumerate(input):
        for colNum, element in enumerate(row):
            if element == " " :
                freeElements.append( (rowNum,colNum) )
    for fe in freeElements :
        print( fe )
    
    # Find and print open rectangles
    for openRect in findOpenRectangles(freeElements,len(input)) :
        print( openRect )        
    

    输出如下(一些格式化以节省空间和添加的标题):

    freeElements

    (0,0),(0,1),(0,2),(0,4),(0,5),(0,6),(0,7)
    (1,0),(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7) (2,0),(2,2),(2,3),(2,4),(2,5),(2,6),(2,7)
    (3,0),(3,1),(3,2),(3,3),(3,4),(3,6)
    (4,0),(4,1),(4,2),(4,3),(4,4),(4,5),(4,6),(4,7) (5,0),(5,1),(5,2),(5,3),(5,4),(5,5),(5,6),(5,7) (6,0),(6,1),(6,2),(6,3),(6,5),(6,6),(6,7)
    (7,0),(7,1),(7,2),(7,4),(7,5),(7,7)
    (8,0),(8,2),(8,3),(8,4),(8,5),(8,6)

    freeRunsPerRow

    (0,{Rect(范围(0,0),范围(0,2)),Rect(范围(0,0),范围(4,7))})
    (1,{Rect(Range(1,1),Range(0,7))})
    (2,{Rect(范围(2,2),范围(2,7)),矩形(范围(2,2),范围(0,0))})
    (3,{Rect(范围(3,3),范围(6,6)),Rect(范围(3,3),范围(0,4))})
    (4,{Rect(Range(4,4),Range(0,7))})
    (5,{Rect(范围(5,5),范围(0,7))})
    (6,{矩形(范围(6,6),范围(5,7)),矩形(范围(6,6),范围(0,3))})
    (7,{Rect(Range(7,7),Range(7,7)),Rect(Range(7,7),Range(4,5)),Rect(Range(7,7),Range(0) ,2))})
    (8,{Rect(Range(8,8),Range(0,0)),Rect(Range(8,8),Range(2,6))})

    findOpenRectangles(freeElements,LEN(输入))

    的Rect(范围(0,1),范围(0,2))
    矩形(范围(1,1),范围(0,7))
    矩形(范围(0,2),范围(4,7))
    矩形(范围(1,2),范围(2,7))
    矩形(范围(3,5),范围(0,4))
    矩形(范围(0,5),范围(4,4))
    矩形(范围(4,5),范围(0,7))
    矩形(范围(1,5),范围(2,4))
    矩形(范围(1,6),范围(2,3))
    矩形(范围(4,6),范围(5,7))
    矩形(范围(0,6),范围(6,6))
    矩形(范围(3,6),范围(0,3))
    矩形(范围(3,7),范围(0,2))
    矩形(范围(4,7),范围(7,7))
    矩形(范围(8,8),范围(2,6))
    矩形(范围(7,8),范围(4,5))
    矩形(范围(0,8),范围(2,2))
    矩形(范围(4,8),范围(5,5))
    Rect(范围(0,8),范围(0,0))

    测试/示例结论

    输出正确。存在所有非平凡的矩形。