我有一个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数组,然后使用我链接的算法。这对我的初始测试足够有效;但是,我有兴趣阅读其他可能的解决方案,我是否需要改变我的性能或空间问题的实现。
答案 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)
我之前的回答是简单的方法。这完全不同。
识别水平线并转换为矩形表示法。
使用行0中的矩形初始化currRects
列表。
将currRow设置为1。
对于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
确定每个案例的必要逻辑基于ayi
,ayt
,byi
和byt
而且很简单,因此这里没有描述。
此函数将返回最多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)
red
和pres
是将前一个矩形b
与传入的a
组合在一起的结果。可以pres
测量矩形的宽度(在y坐标的意义上),或red
使用它,但差异在算法上并不重要。
步骤4的核心是保持列表nextCurrRects
包含函数intersect
返回的所有矩形。当然,nextCurrRects
应该是空的。此外,如果对于前一个b
中的矩形currRects
,返回的标记永远不会false
,则需要将b
放入最终结果列表中(或者只是打印或“产生”)。
使currRects
等于nextCurrRects
。如果currRow
尚未到达,请进行迭代。
currRects
中的所有矩形也属于结果。
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 )
输出如下(一些格式化以节省空间和添加的标题):
(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)
(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))})
的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))
输出正确。存在所有非平凡的矩形。