1. 程式人生 > >Leetcode 939:最小面積矩形(最詳細的解法!!!)

Leetcode 939:最小面積矩形(最詳細的解法!!!)

給定在 xy 平面上的一組點,確定由這些點組成的矩形的最小面積,其中矩形的邊平行於 x 軸和 y 軸。

如果沒有任何矩形,就返回 0。

示例 1:

輸入:[[1,1],[1,3],[3,1],[3,3],[2,2]]
輸出:4

示例 2:

輸入:[[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
輸出:2

提示:

  1. 1 <= points.length <= 500
  2. 0 <= points[i][0] <= 40000
  3. 0 <= points[i][1] <= 40000
  4. 所有的點都是不同的。

解題思路

首先做一些準備工作。我們知道如果所有的點在同一行或者同一列上,那麼我們就沒有解。

n = len(points)
nx = len(set(x for x, y in points))
ny = len(set(y for x, y in points))
if nx == n or ny == n:
    return 0

我們首先想到的解法就是暴力破解,首先遍歷points,從中選出第一個訪問點[x1,y1],將所有訪問過的[x1,y1]新增到一個set中,,我們每次從set中選出第二個點[x2,y2](也就是先確定對角線上的點),然後判斷[x1,y2][x2,y1]

是不是在set中,這樣我們就可以判斷出是否存在由[x1,y1]->[x2,y2]形成的矩形。最後取所有面積的最小值即可。

class Solution:
    def minAreaRect(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        mem = set()
        result = float("inf")
        for x1, y1 in points:
            for x2, y2 in
mem: if (x1, y2) in mem and (x2, y1) in mem: area = abs(x1 - x2)*abs(y1 - y2) if area and area < result: result = area mem.add((x1, y1)) return result if result != float("inf") else 0

另外我們也可以想到這樣的思路,就是找平行於x軸和平行於y軸的平行線,然後在這些平行線中找間隔最小的平行於xy軸的平行線,將這兩個間隔相乘即為最小面積。

首先我們先要遍歷全部的point,然後將所有相同的x軸的點加入到一個list中,由於橫座標的數量不定,所以我們選用defaultdict去存放所有的list

1:1, 3
3:3, 1
2:2

注意,因為python沒有自帶ordered_setordered_map這兩種結構,所有我們要對上述的dict和所有的list先排序,然後再從左到右從下到上遍歷所有的直線。我們首先採取的策略是,先從所有的橫座標中選取出兩個,然後取這兩個橫座標對應的縱座標的交集,然後從交集中選取兩個元素,最後就是取橫座標差乘以縱座標差的最小值。

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        columns = defaultdict(set)
        for x, y in points:
            columns[x].add(y)
        result = float('inf')
        
        columns_keys_list = list(columns.keys())
        for i, x1 in enumerate(columns_keys_list):
            for x2 in columns_keys_list[i+1:]:
                y = sorted(list(columns[x1] & columns[x2]))
                for i in range(1,len(y)):
                    result = min(result, abs(x1 - x2)*abs(y[i] - y[i-1]))

        return result if result != float('inf') else 0    

另外一種思路是遍歷所有的橫座標,例如當我們遍歷到x時,我們再遍歷想x對應的所有縱座標,從縱座標中選出y1,y2看我們之前訪問過的橫座標中是否有相同的縱座標,如果有的話,那麼我們就可以構成矩形,我們計算此時矩形的面積,接著我們將y1,y2新增到訪問記錄中。

這樣做法的好處在於,我們的訪問記錄可以使用set這種結構,這樣我們在查詢的時候就會非常迅速。

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        columns = defaultdict(list)
        for x, y in points:
            columns[x].append(y)

        seen, result = {}, float('inf')

        for x2 in sorted(columns):
            column = columns[x2]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in seen:
                        result = min(result, (x2 - seen[y1,y2]) * (y2 - y1))
                    seen[y1, y2] = x2
        return result if result != float('inf') else 0

不過這個程式碼使用c++實現相當不容易。

reference:

https://leetcode.com/problems/minimum-area-rectangle/discuss/192021/Python-O(N1.5)-80ms

https://leetcode.com/problems/minimum-area-rectangle/discuss/192026/C++-hash-map-+-set-intersection-56-ms

我將該問題的其他語言版本新增到了我的GitHub Leetcode

如有問題,希望大家指出!!!