跳转至

面试题 16.03. 交点

Tags: Hard Math

Links: https://leetcode-cn.com/problems/intersection-lcci/


给定两条线段(表示为起点start = {X1, Y1}和终点end = {X2, Y2}),如果它们有交点,请计算其交点,没有交点则返回空值。

要求浮点型误差不超过10^-6。若有多个交点(线段重叠)则返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。

示例 1:

输入:
line1 = {0, 0}, {1, 0}
line2 = {1, 1}, {0, -1}
输出: {0.5, 0}

示例 2:

输入:
line1 = {0, 0}, {3, 3}
line2 = {1, 1}, {2, 2}
输出: {1, 1}

示例 3:

输入:
line1 = {0, 0}, {1, 1}
line2 = {1, 0}, {2, 1}
输出: {},两条线段没有交点

提示:

  • 坐标绝对值不会超过 2^7
  • 输入的坐标均是有效的二维坐标

#include <cmath>
class Solution {
    struct Point {
        double x, y;
        Point(double x = 0, double y = 0): x(x), y(y) {}

        Point operator+(Point p) { return Point(x + p.x, y + p.y); }
        Point operator-(Point p) { return Point(x - p.x, y - p.y); }
        Point operator*(double k) { return Point(x * k, y * k); }
        Point operator/(double k) { return Point(x / k, y / k); }

        double abs() { return sqrt(norm()); }
        double norm() { return x * x + y * y; }

        bool operator<(const Point p) const { return x != p.x ? x < p.x : y < p.y; }

        bool operator==(const Point p) const 
        { return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS; }
    };
    typedef Point Vector;

    struct Segment {
        Point p1, p2;  
    };

    constexpr static double EPS = 1e-6;
    inline bool equals(double a, double b) { return fabs(a - b) < EPS; } 
public:
    double cross(const Vector & a, const Vector & b)
    { return a.x * b.y - a.y * b.x; }

    double dot(const Vector & a, const Vector & b)
    { return a.x * b.x + a.y * b.y; }

    int ccw(Point & p0, Point & p1, Point & p2)
    {
        Vector a = p1 - p0;
        Vector b = p2 - p0;
        if (cross(a, b) > EPS) return -1;
        else if (cross(a, b) < -EPS) return 1;
        else {
            if (dot(a, b) < -EPS) return 2;
            else if (a.norm() < b.norm()) return -2;
            else return 0;
        }
    }

    bool intersection(Point & p1, Point & p2, Point & p3, Point & p4)
    {
        return ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 
            && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0;
    }

    Point getCrossPoint(Segment s1, Segment s2)
    {
        Vector base = s2.p2 - s2.p1;
        double d1 = abs(cross(base, s1.p1 - s2.p1));
        double d2 = abs(cross(base, s1.p2 - s2.p1));
        if (d1 < EPS && d2 < EPS) {
            Point tmp1 = ccw(s1.p1, s1.p2, s2.p1) == 0 ? s2.p1 : s2.p2;
            Point tmp2 = ccw(s2.p1, s2.p2, s1.p1) == 0 ? s1.p1 : s1.p2;
            return tmp1 < tmp2 ? tmp1 : tmp2;
        }

        double t = d1 / (d1 + d2);
        return s1.p1 + (s1.p2 - s1.p1) * t;
    }

    vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
        std::ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout.tie(NULL);

        Point p1(start1[0], start1[1]), p2(end1[0], end1[1]), p3(start2[0], start2[1]), p4(end2[0], end2[1]);
        if (!intersection(p1, p2, p3, p4)) return {};

        Segment s1, s2;
        s1.p1 = p1; s1.p2 = p2;
        s2.p1 = p3; s2.p2 = p4;
        Point res = getCrossPoint(s1, s2);

        return vector<double>{res.x, res.y};
    }
};

线段相交的模板题。额外注意线段有部分重合的情况需要特判。