593. 有效的正方形

593. 有效的正方形

给定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

 

一道很有意思的题目,看到这题,我最直观的想法是:

根据四个点生成两条线段,如果下面条件成立:

  • 1、两条线段长度相等

  • 2、两条线段垂直

  • 3、两条线段中点是一样的

 

遍历所有可能性(其实只有三种),只要有一种成立,就可以组成正方形。

 

ps:中间有几个坑,我写的时候全犯了:

  • 1、垂直不能用斜率计算,因为平行于y轴的线段斜率无穷大,用坐标计算会出现  『除 0 』,因此用 向量点积  x1 * x2 + y1 * y2 = 0

  • 2、两条线段垂直,长度相等,还需要判断中点重合

  • 2、判断题目到底给了几个不同的点,用set去重。

 

代码如下:

class Solution:
    def validSquare(self, p1, p2, p3, p4):
        if len(set([tuple(p1),tuple(p2),tuple(p3),tuple(p4)])) < 4:
            return False

        if self.cacl_dis(p1, p2) == self.cacl_dis(p3, p4) and self.is_square(p1, p2, p3, p4) and self.cacl_mid(p1, p2, p3, p4):
            return True
        if self.cacl_dis(p1, p3) == self.cacl_dis(p2, p4) and self.is_square(p1, p3, p2, p4) and self.cacl_mid(p1, p3, p2, p4):
            return True
        if self.cacl_dis(p1, p4) == self.cacl_dis(p2, p3) and self.is_square(p1, p4, p2, p3) and self.cacl_mid(p1, p4, p2, p3):
            return True
        return False

    def cacl_mid(self, p1, p2, p3, p4):
        x1, y1 = (p2[0] + p1[0])/2, (p2[1] + p1[1])/2
        x2, y2 = (p3[0] + p4[0])/2, (p3[1] + p4[1])/2
        return x1 == x2 and y1 == y2

    def cacl_dis(self, p1, p2):
        return sqrt((p2[1] - p1[1]) ** 2 + (p2[0] - p1[0]) ** 2)

    def is_square(self, p1, p2, p3, p4):
        x1, y1 = p2[0] - p1[0], p2[1] - p1[1]
        x2, y2 = p4[0] - p3[0], p4[1] - p3[1]
        return x1*x2 + y1*y2 == 0

 

 

 

然后就在评论区看到了很多有意思的解法:

方法一:

分享一种不用计算边长,也不用考虑点的顺序的方法:

如果以正方形中心点为坐标原点,则正方形的任意顶点绕原点旋转 90° 后仍然在这四个点中。 因此只需要检查每个点旋转 90° 后,是否还存在于四个点组成的集合中即可。

而二维平面旋转 90° 很容易表示: (x, y) 逆时针旋转 90° 后为 (-y, x)

#define Cen2Ori(p) {p[0]*4-cenX, p[1]*4-cenY} 
class Solution {
public:
    bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) {
        // 中心点,缩放 4 倍,避免除法
        int cenX = p1[0] + p2[0] + p3[0] + p4[0]; 
        int cenY = p1[1] + p2[1] + p3[1] + p4[1];
        // 将中心点移动到坐标原点
        vector<array<int, 2>> pts = {Cen2Ori(p1), Cen2Ori(p2), Cen2Ori(p3), Cen2Ori(p4)};
        // 将四个顶点存入哈希表
        auto arrayHash = [fn = hash<long>{}] (const array<int, 2>& arr) -> size_t { // 哈希函数
            return fn(*((long const*)arr.data()));
        };
        unordered_set<array<int, 2>, decltype(arrayHash)> pts_set(pts.begin(), pts.end(), 0, arrayHash);
        if(pts_set.size() < 4) return false;
        // 检查每个点旋转 90 度以后的点是否在哈希表中
        for(auto &pt: pts){  
            if(!pts_set.count({-pt[1], pt[0]})) return false;
        }
        return true;
    }
};

 

 

方法二:

每两个点算一下距离,要么是边要么是对角线,如果出现0肯定不是,如果是正方形只有两种距离的结果。

class Solution {
    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
        Set<Integer> set = new HashSet<Integer>();
        set.add(distanceSquare(p1, p2));
        set.add(distanceSquare(p1, p3));
        set.add(distanceSquare(p1, p4));
        set.add(distanceSquare(p2, p3));
        set.add(distanceSquare(p2, p4));
        set.add(distanceSquare(p3, p4));
        return set.size() == 2 && !set.contains(0);
    }
    
    private static int distanceSquare(int[] a, int[] b) {
        int i = a[0] - b[0];
        int j = a[1] - b[1];
        return i * i + j * j;
    }
}

可能有点问题:

1、如果出现一个正三角形 然后最后一个点在正三角形中间会如何呢 依旧满足 边长相等 且数值为2 但不是正方形

2、两个等边三角形拼起来符合你这个,但不是正方形

 

方法三:

任选三个点都是直角三角形

ublic class Solution {
    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
        //任选三个点 都是 一个直角三角形
        return isRightTriangle(p1, p2, p3) && isRightTriangle(p1, p2, p4) && isRightTriangle(p1, p3, p4) && isRightTriangle(p2, p3, p4);
    }

    public boolean isRightTriangle(int[] p1, int[]p2, int[] p3){
        int d1 = (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]);
        int d2 = (p2[0] - p3[0]) * (p2[0] - p3[0]) + (p2[1] - p3[1]) * (p2[1] - p3[1]);
        int d3 = (p3[0] - p1[0]) * (p3[0] - p1[0]) + (p3[1] - p1[1]) * (p3[1] - p1[1]);
        if(d1 > d2 && d2 == d3 && d2 + d3 == d1 ||
            d2 > d1 && d1 == d3 && d1 + d3 == d2 ||
            d3 > d1 && d1 == d2 && d1 + d2 == d3){
            return true;
        }
        return false;
    }
}

 

posted @ 2022-08-01 17:05  r1-12king  阅读(94)  评论(0编辑  收藏  举报