携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
题目描述
给定2D空间中四个点的坐标 p1, p2, p3 和 p4,如果这四个点构成一个正方形,则返回 true 。
点的坐标 pi 表示为 [xi, yi] 。输入 不是 按任何顺序给出的。
一个 有效的正方形 有四条等边和四个等角(90度角)。
示例 1:
输入: p1 = [0,0], p2 = [1,1], p3 = [1,0], p4 = [0,1]
输出: True
示例 2:
输入:p1 = [0,0], p2 = [1,1], p3 = [1,0], p4 = [0,12]
输出:false
示例 3:
输入:p1 = [1,0], p2 = [-1,0], p3 = [0,1], p4 = [0,-1]
输出:true
提示:
- p1.length == p2.length == p3.length == p4.length == 2
- -104 <= xi, yi <= 104
思路
一拿到题目,思路是按顺序连线形成4条边,看长度是否相等且相邻的边相互垂直。但是题目给出的4个点并补保证顺序,那么怎么来校验呢?
我们发现,4个点,不讲顺序的话,两两相连,可以形成6条线段,在正方形中,这6条线段中,4条是边,2条是对角线。这4条边长度相等,2条对角线长度相等,且对角线长度 = 边长 * sqrt(2)
。这是这个图形是正方形的必要条件,同时,刚好是充分的,我们可以想象一般的平行四边形、矩形、菱形都不会同时满足这些条件。 按照上述方法,求出6条边的长度,校验是否4短2长,且满足长 = 短 * sqrt(2)
。
还有一个注意点是,编码中,求2点距离和sqrt(2)的时候,由于数据类型都存在小数不精确的问题,我们采用d^2来代替d,避免开方操作带来的不精确,校验的条件演变成长^2 = 短^2 * 2
。
Java版本代码
class Solution {
public static boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
Map<Integer, Integer> map = new HashMap<>();
putDistance(map, p1, p2);
putDistance(map, p1, p3);
putDistance(map, p1, p4);
putDistance(map, p2, p3);
putDistance(map, p2, p4);
putDistance(map, p3, p4);
if (map.size() != 2) {
return false;
}
int[] l = new int[2];
int index = 0;
for (Integer item : map.keySet()) {
l[index++] = item;
}
// 保证小的在前
if (l[0] > l[1]) {
// 交换
int temp = l[0];
l[0] = l[1];
l[1] = temp;
}
if (l[0] * 2 != l[1]) {
return false;
}
if (map.get(l[0]) != 4) {
return false;
}
return true;
}
private static void putDistance(Map<Integer, Integer> map, int[] p1, int[] p2) {
Integer d = getDistance(p1, p2);
map.put(d, map.getOrDefault(d, 0) + 1);
}
private static int getDistance(int[] p1, int[] p2) {
return (p1[0] - p2[0])*(p1[0] - p2[0]) + (p1[1] - p2[1])*(p1[1] - p2[1]);
}
}