给定一个点数组,找到最大矩形面积

5
假设我有一个对象数组,名为Point。Point对象有x和y值。
为了使问题更加复杂,假设没有初始边界,也就是说我们想要找到的矩形区域由Point对象限定。因此,至少有4个Point对象。
例如,如果数组只有4个Point对象:Point(0,0),Point(100,0) Point(0,100)和Point(100,100),我们想要返回100*100,即矩形面积。
这是简单的部分。但是考虑到存在多于4个Point对象的情况。如何找到最大的矩形面积?
public int maxArea(Point[] points)
{
    int area;
    //do algo
    return area;
}

编辑:仅通过查看 y 和 x 的最小值和最大值并不能保证因为我正在寻找不包含 Point 对象的矩形区域。因此,在该最大矩形区域内没有任何内容。


1
通过Point对象来限定的最大区域面积。使用Point(0,0),Point(100,0),Point(0,100)和Point(100,100)来限定,我认为它是100*100,所以不是无限的... - ealeon
1
你所说的“bounded”是什么意思?假设你有(0,0),(0,100),(100,0),(100,100),(50,50),最大的矩形是多少? - flight
如果面积为50*50,则返回该面积。如果您有相同的最大面积,则仍然返回它。 - ealeon
越快越好,但不是必须的。 - ealeon
1
矩形是否应该平行于坐标轴?还是可以倾斜? - st0le
显示剩余6条评论
5个回答

3

首先,我认为问题的表述应该是这样的:

  • 要找到的矩形有水平(恒定y)和垂直(恒定x)的边界,即没有“旋转”。

  • 矩形至少在每条边的“内部”(“打开”部分)上有一个点,不在角落处。 (它可能在任何边上有多个点,并且也可能在其角落处有点。) 这排除了“无限”解决方案,因为所有点都有有限的x,y。 它还排除了我们可能想要仅通过左上角和右下角点以及类似结构来定义矩形的情况。

  • 我们要在满足上述条件的所有矩形中寻找面积最大的矩形。

假设上述是问题的正确(重新)表述,我认为这是一个二维优化问题,可能有许多“局部”最优解。 任何类型的“从某个地方开始逐渐改进”的方法都只会找到局部最优解,但不一定是全局最优解。

我没有能够想出比O(N^2)更好的方法,大致需要N次本地优化,其中每个本地优化为O(N)。 我将用一些代码片段(部分伪代码或备注)草图来说明本地优化的基本部分。 其余部分是“更多的相同”,应该不难填写。

为了减少文字而不变得不准确,从现在开始,“边缘上的点(矩形的边缘)”指的是在边缘的“内部”部分的点,而不是在角落处。 同样,通过“矩形”,我将指一个“有资格”的矩形,即满足基本条件的矩形:没有内部点,并且每条边上至少有一个点。

现在,局部优化是由来自点集的特定点以及{Left,Top,Right,Bottom}中的特定“边框类型”共同“定义”的。 假设我们选择了一个点L和边框类型“Left”;局部最优解被定义为具有L在其左边缘的“最佳”(最大面积)矩形。

我们将构建所有(L,Left)-矩形,并在途中跟踪找到的最大矩形。

请注意:任何(L,Left)-矩形,其右边缘上有点R,必须具有点T和B在其顶部和底部边界上,其中

L.x < T.x < R.x
L.x < B.X < R.X
B.y < L.y < T.y
B.y < R.y < T.Y

假设我们按照x顺序扫描点,从L之后开始。
一方面,在找到R之前,上述条件表明我们必须先遇到T和B。
另一方面,一旦我们找到R,从所有y > L.y的点中,我们在中间遇到的点现在将被最低y值所限制。底部边界同理。
假设N是点{P}的数量, 让index_X[]成为按x排序的点的索引数组,使得当i小于j时,P [index_X [i]] x <= P [index_X [j]].x。
此外,让iL成为x排序数组中L的索引:P [index_X [iL]] = L。
在以下代码片段(VB语法;无论您使用什么语言,都不应该太难进行翻译)中,我们首先确定“某个”T(顶部点)和“某个”B(底部点)。 然后,我们继续扫描,并且每当找到一个完成矩形的点R时,我们: (a) 计算要更新的最优解的面积(如果更大); (b) 用找到的R替换T或B(取决于R是否在L上方),并重复查找R。
    Dim indx As Integer = iL + 1 'first point to consider on the right of L
    Dim T As PointF = Nothing
    Dim B As PointF = Nothing
    ' find T,B
    While (indx < N AndAlso (T = Nothing OrElse B = Nothing))

      Dim Q As PointF = Pts(indx_X(indx))

      If (Q.Y > L.Y) Then
        ' a candidate for T
        If (T = Nothing) OrElse (Q.Y < T.Y) Then
          T = Q
        End If

      ElseIf (Q.Y < L.Y) Then
        ' a candidate for B
        If (B = Nothing) OrElse (Q.Y > B.Y) Then
          B = Q
        End If

      Else
        Return -1 'Failure for L altogether: Q has exactly the same y-value as L and we didn't find yet both T and B.
      End If

      indx += 1
    End While

    If (T = Nothing OrElse B = Nothing) Then
      Return -1  'Failure for L: we have exhausted all points on the right without finding both T and B.
    End If

    ' we have "some" T and B, now proceed to find all (L,Left) rectangles
    ' intialize result (= max area from (L,Left)-rectangles).
    Dim result As Double = -1
    ' the next point to consider
    indx += 1
    ' find successive rectangles, by searching for R, given L (fixed) and T,B (occasionally updated). 
    While (indx < N)

      Dim R As PointF = Pts(indx_X(indx)) 'candidate

      If (R.Y = L.Y) Then
        ' rectangle found, process it.
        Dim area As Double = (R.X - L.X) * (T.Y - B.Y)
        If area > result Then
          result = area
        End If
        ' it all stops here: no further rectangles {L,Left) are possible as they would have this R inside.
        Exit While
      End If

      If (R.Y > L.Y) Then
        ' a point with y > L.Y:
        ' if it is above T we can ignore it.
        ' if is below T, it is the R-bound for the rectangle bounded by L,T,B
        If (R.Y < T.Y) Then
          ' process the rectangle
          Dim area As Double = (R.X - L.X) * (T.Y - B.Y)
          If area > result Then
            result = area
          End If
          ' move on, understanding that for any NEXT rectangle this R must be the upperbound.
          T = R
        End If

      Else 'R.Y < L.Y
        ' a point with y < L.Y
        ' if it is below B we can ignore it.
        ' if it is above B, it is the R-bound for the rectangle bounded by L,T,B
        If (R.Y > B.Y) Then
          ' process the rectangle
          Dim area As Double = (R.X - L.X) * (T.Y - B.Y)
          If area > result Then
            result = area
          End If
          ' move on, understanding that for any NEXT rectangle this R must be the lowerbound.
          B = R
        End If

      End If

      indx += 1
    End While

    Return result

总体解决方案当然是:对于每个点P,找到opt(P, Left),opt(P, Right),opt(P, Top),opt(P, Bottom)中的最优解,然后在所有P中找到最大值。 对于Right,Top和Bottom的变量当然与我上面概述的opt(Left)非常相似。 预排序(为了获取x-order和y-order的索引(用于处理(P,Top)和(P,Bottom)情况))是O(nLogn),每个本地优化都是O(n)-查看代码。因此,总体复杂度为O(n^2)。
注:由于原始表述对我来说不是绝对清晰: 如果矩形也可以由CORNER点限定,则以上需要进行一些微小的调整(主要是向不等式条件添加等号),但本质上不会改变算法。

2

我能在 O(N^2*Log(N)) 的时间复杂度内完成这个任务。

首先将所有点放入一个集合中,这样我们可以在 O(Log(N)) 的时间内检查一个点是否存在。

枚举对角线上的两个点,这需要 O(N^2) 的时间,然后就可以确定矩形,检查另外两个点是否存在,如果存在,就知道是否可以获取它,如果可以,更新答案。


1

你其实不需要四个点。

找到左上角和右下角的点,形成一个矩形。

遍历数组,只需跟踪/更新这两个点。

一旦退出循环,只需返回(topLeft.x - bottomRight-x)*(topLeft.y - bottomRight-y)。

就是这样。


首先,OP已经说过矩形不应包含其他点(来自参数),并且矩形的角落应该是参数的一部分。 - st0le
是的....如果points[i].x > topLeft.x && point[i].y < topLeft.y,则跟踪/更新。对于bottomRight也差不多做同样的事情。这将确保topLeft和bottomRight形成的矩形中不会有任何点。 - antz
当然,但是你如何证明它是最大面积? - st0le
你是对的。它不会。但是,如果在更新您的topLeft/bottomRight时有另一个检查条件,则可以解决此问题。因此,如果points[i].x > topLeft.x && point[i].y < topLeft.y,则tempNewTopLeft = point[i]。因此,如果(tempNewTopLeft.x - bottomRight-x)(tempNewTopLeft - bottomRight-y) > (topLeft.x - bottomRight-x)(topLeft.y - bottomRight-y),则topLeft = tempNewTopLeft; - antz
我觉得这只是一堆if比较而已。在遍历数组时,如果point [i]有资格成为topLeft或bottomRight,则将其作为临时变量并将其与当前的topLeft和bottomRight进行比较。如果临时变量具有更大的矩形面积,则替换topLeft / bottomRight。因此,1)有if来判断point [i]是否有资格,2)还有if来判断它是否具有更大的矩形面积。 - antz

0

最简单的方法是在 while 循环中计算所有可能的面积,然后在最后获取最大值。


我认为这不正确,问题有点不清楚,但似乎解决方案矩形不能包含集合中的其他点。 - Mathias
1
我的意思是创建一个循环,计算所有可能的矩形面积(没有点在其中),然后只获取最大的那个。有许多其他方法可以做到这一点,但我认为这可能是实现目标最快的方法(只需忽略此解决方案的性能较慢即可)。 - Michel Gokan Khan

0
def max_area_rectangle(points):
    seen_points = set()
    max_area = float('-inf')
    for x1, y1 in points:
        for x2, y2 in seen_points:
            if (x1, y2) in seen_points and (x2, y1) in seen_points:
                area = abs(x1 - x2) * abs(y1 - y2)
                if area > 0 and area > max_area:
                    max_area = area
        seen_points.add((x1, y1))
    #return area if it was possible to compute otherwise None
    return max_area if max_area > float('-inf') else None

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接