给定一系列点,找到最大矩形区域

时间:2012-10-20 02:56:14

标签: algorithm

假设我有一个名为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对象的矩形区域。所以最大矩形区域内没有任何东西

5 个答案:

答案 0 :(得分:3)

让我首先阐述问题,因为我认为这意味着:

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

  • 矩形在每个边缘的“内部”(“开放”)部分至少有一个点,即不在角落。 (它可能在任何边缘都有多个点,并且也指向其角落。) 这排除了“无限”解,因为所有点都有有限的x,y。 它还排除了我们打算仅通过TopLeft和BottomRight点以及类似结构来定义矩形的情况。

  • 我们寻找满足上述条件的最大面积的矩形。

假设以上是问题的正确(重新)公式,我认为这是一个二维优化问题,可能存在许多“局部”最优。 任何类型的“从某事开始并逐渐改进”的方法只能找到局部最优,但不一定是全局的。

我还没有能够提出比O(N ^ 2)方法更好的方法,粗略地讲 - N次本地优化,其中每个局部优化都是O(N)。 我将使用一些代码片段(部分伪代码或备注)为本地优化的基本部分描绘方法。 其余部分“更多相同”,不应该难以填写。

为了减少措辞而不会变得不准确,从此我将意味着“边缘(一个recctangle)上的一个点”在边缘的“内部”部分而不是在角落处。 同样,通过'矩形',我将指一个“符合条件的”反应角,即满足基本条件的反应角:内部没有点,每个边上至少有一个点。

现在,LOCAL优化由点集中的特定点“定义”,并结合来自{Left,Top,Right,Bottom}的特定“border-type”。 假设我们选择了一个L点和边界类型“Left”;局部最优解决方案被定义为在其左边缘具有L的“最佳”(最大区域)矩形。

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

观察:任何(L,左) - 在其右边界上有点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

现在,我们以L顺序扫描点,从L开始。

一方面:在我们找到R之前,上述条件表明我们必须首先遇到T和B.

另一方面,一旦我们找到了R,所有的点都是y&gt;我们在中间遇到过的,我们现在将受到y最低的那个的约束。同样适用于底线。

N点数{P}, 令index_X []为x排序点的索引数组,使得P [index_X [i]] x&lt; = P [index_X [j]] .x只要i小于j。

此外,让iL成为x排序数组中L的索引:P [index_X [iL]] = L.

在下面的代码片段中(VB语法;使用您使用的任何语言翻译都不会太难),我们首先确定“某些”T(顶部边缘点)和“部分”B(一个底边点)。 然后,我们继续扫描,每当我们找到完成矩形的点R时,我们: (a)如果面积较大,则计算更新最佳面积; (b)用找到的R代替T或B(取决于R是高于L还是低于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(左)非常相似。 预排序(获取x顺序和y顺序(用于处理(P,Top)和(P,Bottom) - 的索引)是O(nLogn),局部优化是O(n) - 查看代码。因此总体复杂度为O(n ^ 2)。

注意添加 - 因为原始配方对我来说并不是绝对清楚: 如果矩形也可以由CORNER点限定,那么上面需要一些微小的调整(大多数是对不等式条件添加等号),但它本质上不会改变算法。

答案 1 :(得分:1)

你真的不需要四分 找到形成矩形形状的topLeft和bottomRight Point 循环遍历数组,只需跟踪/更新这两点 一旦退出循环就回来了 (topLeft.x - bottomRight-x)*(topLeft.y - bottomRight-y);

你去了

答案 2 :(得分:1)

我可以在O(N^2*Log(N))中执行此操作。

首先将所有点放在一个集合中,以便我们检查O(Log(N))中是否存在一个点。

枚举对角线上的2个点,花费O(N^2),然后确定矩形,检查是否存在其他2个点然后你知道是否可以得到它,如果你这样做,则更新答案。

答案 3 :(得分:0)

最简单的方法是计算while循环中的所有可能区域,并在结尾处获取最大区域。

答案 4 :(得分: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