1. 问题描述:
给定在 xy 平面上的一组点,确定由这些点组成的任何矩形的最小面积,其中矩形的边不一定平行于 x 轴和 y 轴。如果没有任何矩形,就返回 0。
示例 1:
输入:[[1,2],[2,1],[1,0],[0,1]]
输出:2.00000
解释:最小面积的矩形出现在 [1,2],[2,1],[1,0],[0,1] 处,面积为 2。
示例 2:
输入:[[0,1],[2,1],[1,1],[1,0],[2,0]]
输出:1.00000
解释:最小面积的矩形出现在 [1,0],[1,1],[2,1],[2,0] 处,面积为 1。
示例 3:
输入:[[0,3],[1,2],[3,1],[1,3],[2,1]]
输出:0
解释:没法从这些点中组成任何矩形。
示例 4:
输入:[[3,1],[1,1],[0,1],[2,1],[3,3],[3,2],[0,2],[2,3]]
输出:2.00000
解释:最小面积的矩形出现在 [2,1],[2,3],[3,3],[3,1] 处,面积为 2。
提示:
1 <= points.length <= 50
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-area-rectangle-ii
2. 思路分析:
① 一开始的时候是没有什么思路,理解了一下官方的题解,官方提供了两种思路。第一种思路:先判断四个点是否能够构成平行四边形,然后再判断平行四边形中的相邻两条边是否垂直,具体的做法是任意取出points列表中的三个点p1,p2,p3(因为使用的是python语言所以可以使用itertools.permutations方法得到points集合中长度为3的所有排列),然后检查以p2,p3为顶点的对角线中的另外一条对角线中的第四个顶点p4是否存在,其实是判断对角线是否相等,使用x1 + x2 == x3 + x4,y1 + y2 == y3 + y4进行判断,同时满足这两个条件才可以判断两条对角线相等,具体的做法是判断p4 = p2 + p3 - p1是否存在于points列表中,假如存在再判断相邻的两条边是否垂直,当使用坐标来判断是否是垂直的时候可以使用:x1 * x2 + y1 * y2 == 0,因为在求解的时候可能存在误差,所以规定在一定误差范围之内的都是符合答案的,所以我们使用x1 * x2 + y1 * y2小于某一个误差的时候肯定是满足答案的(x1 * x2 + y1 * y2 == 0的时候就垂直了)
② 第二种思路是根据矩形的充分必要条件进行判定的:两条对角线的中心相同并且顶点到中心的距离也是相等的,所以需要维护两个变量:两个顶点的中心以及顶点到中心的距离,具体的做法是任意取出points点集中的两个元素计算他们的中心以及顶点到中心的距离,并且将其映射在一个字典中,只有同时存在中心点相同而且顶点到中心点的距离是一样的才会被映射到同一个位置,最终我们可以遍历这个字典取出同时满足这两个条件的组合,相邻两个顶点之间的距离相乘那么就为矩形的面积
③ 官方的第二种思路中字典将一个复数以及float类型的值作为键还是第一次看到,值得学习学习
3. 代码如下:
官方代码如下:
import collections
import itertools
class Solution(object):
def minAreaFreeRect(self, points):
EPS = 1e-7
points = set(map(tuple, points))
ans = float('inf')
for p1, p2, p3 in itertools.permutations(points, 3):
print(p1, p2, p3)
p4 = p2[0] + p3[0] - p1[0], p2[1] + p3[1] - p1[1]
if p4 in points:
# 转化为复数进行求解
v21 = complex(p2[0] - p1[0], p2[1] - p1[1])
v31 = complex(p3[0] - p1[0], p3[1] - p1[1])
if abs(v21.real * v31.real + v21.imag * v31.imag) < EPS:
area = abs(v21) * abs(v31)
if area < ans:
ans = area
return ans if ans < float('inf') else 0
class Solution(object):
def minAreaFreeRect(self, points):
# 坐标点转换为复数之后那么对两个坐标点做差得到的就是两个坐标点之间的距离
points = [complex(*z) for z in points]
seen = collections.defaultdict(list)
for P, Q in itertools.combinations(points, 2):
center = (P + Q) / 2
radius = abs(center - P)
seen[center, radius].append(P)
ans = float("inf")
for (center, radius), candidates in seen.iteritems():
for P, Q in itertools.combinations(candidates, 2):
# 与Q对应的另外一个对角线上的顶点为2*center - Q: 之前计算center的时候取得就是两个点之间的中心, 所以使用这个公式就可以计算得到另外一个坐标的位置
ans = min(ans, abs(P - Q) * abs(P - (2*center - Q)))
return ans if ans < float("inf") else 0