题目
解法1:暴力n*n解法
循环所有点的pair,尝试用这两个点组成矩形
class Solution:
def minAreaRect(self, points: List[List[int]]) -> int:
points_dict = set()
for x,y in points:
points_dict.add((x,y))
min_area = float('inf')
for x1,y1 in points:
for x2,y2 in points:
if x1 > x2 and y1 > y2:
if (x1,y2) in points_dict and (x2,y1) in points_dict:
area = (y1-y2) * (x1-x2)
min_area = min(area,min_area)
return min_area if min_area != float('inf') else 0
这边比较有意思的事,如果把set换成dict会超时,说明这边这种dict搜索tuple的方式实际肯定不是O(1)
解法2:优化的暴力解法
对于每个点的pair,我们只考虑一个是top right和bottom left的情况,减少复杂度,为啥呢
举个例子:在example 1中,一开始先考虑了(1,1)和(3,3)作为对角点的情况,但是后面还是会考虑(1,3)和(3,1)作为对角点的情况,而这两种情况是一样的,所以就有重复计算
二现在的做法是,想办法只考虑(1,1)和(3,3)作为对角点的情况
class Solution:
def minAreaRect(self, points: List[List[int]]) -> int:
points_dict = set()
for p in points:
points_dict.add(tuple(p))
min_area = float('inf')
for x1,y1 in points:
for x2,y2 in points:
# why we only deal with x1 > x2 and y1 > y2 ???
# because to avoid repeated computation, for a pair of point, we only deal with them when:
# p1 is top right corner and p2 is bottom left point
if x1 > x2 and y1 > y2:
if (x1,y2) in points_dict and (x2,y1) in points_dict:
area = (y1-y2) * (x1-x2)
min_area = min(area,min_area)
return min_area if min_area != float('inf') else 0
解法3:
为了减少复杂度,将x或者y进行归类,用一种巧妙的方法去去除无需计算的矩形,这边最巧妙的点在于,利用排序只计算相邻两个x之间或者y之间的矩形,而无序计算其他的,因为其他的会面积更大
class Solution:
def minAreaRect(self, points: List[List[int]]) -> int:
n = len(points)
# number of different x and different y
nx = len(set(x for x,y in points))
ny = len(set(y for x,y in points))
# if nx == n, meaning every point has different value, no way to form a rectangle that's parallel to y axis
if nx == n or ny == n:
return 0
p = collections.defaultdict(list)
# we use the large number to the key because time is in (nx * ny * ny) or (nx * nx * ny)
# so in this way the computation can be minimized
if nx > ny:
for x,y in points:
p[x].append(y)
else:
for x,y in points:
p[y].append(x)
# thhis dict saves {(y1,y2):x}, which means the previous appeared x
# why saved like this??? because for current x, and same y1,y2, the min area is guranteed to formed between the current x and the previous x
# all x earlier than previous_x will have larger area
previous_x2y = {}
min_area = float('inf')
# x need to be in order
for x in sorted(p):
p[x].sort()
for i in range(len(p[x])):
for j in range(i+1,len(p[x])):
y1,y2 = p[x][i],p[x][j]
if (y1,y2) in previous_x2y:
min_area = min(min_area,(x-previous_x2y[(y1,y2)])*(y2-y1))
previous_x2y[(y1,y2)] = x
return min_area if min_area != float('inf') else 0
时间复杂度:O(nx * nx * ny) < O(N ^ 1.5)
具体时间复杂度分析如下:
参考:https://leetcode.com/problems/minimum-area-rectangle/discuss/192021/Python-O(N1.5)-80ms