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 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
- 所有的點都是不同的。
解題思路
首先做一些準備工作。我們知道如果所有的點在同一行或者同一列上,那麼我們就沒有解。
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
軸的平行線,然後在這些平行線中找間隔最小的平行於x
和y
軸的平行線,將這兩個間隔相乘即為最小面積。
首先我們先要遍歷全部的point
,然後將所有相同的x
軸的點加入到一個list
中,由於橫座標的數量不定,所以我們選用defaultdict
去存放所有的list
1:1, 3
3:3, 1
2:2
注意,因為python
沒有自帶ordered_set
和ordered_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
我將該問題的其他語言版本新增到了我的GitHub Leetcode
如有問題,希望大家指出!!!