程序员面试金典 16.3

Intersection:判断两条线段是否相交。

根据解析几何的知识,两条直线相交有两种情况:

  • 斜率不同,则一定会相交,对于线段,需要判断交点是否在线段上,这里使用的是直线的向量表达式(x, y) = t(x1, y1) + (1 - t)(x2, y2),线段上的点t取值为[0, 1]
  • 斜率相同,则截距相同时会相交,也就是重合,要返回最靠近左下的点,这个逻辑判断比较麻烦,但是无法避免。LineSegment的构造函数将端点进行了排序,最后在procSame()返回了最靠左下的交点

浮点数可以除0.0,所以对于垂直线(斜率无穷大)也能正常除,只是此时的截距是NaN。浮点数的inf无法进行相等判断,但是可以使用finite()判断。

struct Point
{
    double x, y;
    Point(double dx = 0.0, double dy = 0.0) : x(dx), y(dy){}
    Point(int ix, int iy)
    {
        x = (double)ix;
        y = (double)iy;
    }
    bool leftdown(const Point &p) const
    {
        if(x < p.x) return true;
        else if(x > p.x) return false;
        else return y < p.y;
    }
};
struct LineSegment
{
    Point el, er;
    double slope, intercept;
    LineSegment(const Point &p1, const Point &p2)
    {
        if(p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y)){
            el = p1, er = p2;
        }
        else{
            el = p2, er = p1;
        }
        slope = (er.y - el.y) / (er.x - el.x);
        intercept = er.y - slope * er.x;
    };
    bool vertical() const
    {
        return !finite(slope);
    }
    bool contain(const Point &p) const
    {
        double t = (p.x - el.x) / (er.x - el.x);
        if(!finite(t)) t = (p.y - el.y) / (er.y - el.y);
        return 0.0 <= t && t <= 1.0;
    }   
};
class Solution {
public:
    vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
        Point s1(start1[0], start1[1]), e1(end1[0], end1[1]);
        Point s2(start2[0], start2[1]), e2(end2[0], end2[1]);
        LineSegment ls1(s1, e1), ls2(s2, e2);
        if(ls1.vertical() || ls2.vertical()){
            return procVertical(ls1, ls2);
        }
        else if(abs(ls1.slope - ls2.slope) <= EPS && abs(ls1.intercept - ls2.intercept) <= EPS){
            return procSame(ls1, ls2);
        }
        else{
            Point inter;
            inter.x = (ls2.intercept - ls1.intercept) / (ls1.slope - ls2.slope);
            inter.y = inter.x * ls1.slope + ls1.intercept;
            if(ls1.contain(inter) && ls2.contain(inter)){
                return vector<double>{ inter.x, inter.y };
            }
            else return vector<double>();
        }
    }
private:
    const double EPS = 1E-6;
    vector<double> procVertical(const LineSegment &ls1, const LineSegment &ls2)
    {
        Point inter;
        if(ls1.vertical() && ls2.vertical()){
            if(abs(ls1.el.x - ls2.el.x) <= EPS){
                return procSame(ls1, ls2);
            }
        }
        else if(ls1.vertical()){
            inter.x = ls1.el.x;
            inter.y = ls2.slope * inter.x + ls2.intercept;
            if(ls1.contain(inter)){
                return vector<double>{ inter.x, inter.y };
            }
        }
        else {
            inter.x = ls2.el.x;
            inter.y = ls1.slope * inter.x + ls1.intercept;
            if(ls2.contain(inter)){
                return vector<double>{ inter.x, inter.y };
            }
        }
        return vector<double>();
    }
    vector<double> procSame(const LineSegment &ls1, const LineSegment &ls2)
    {
        if(ls1.el.leftdown(ls2.el)){
            if(ls1.contain(ls2.el)){
                return vector<double>{ ls2.el.x, ls2.el.y };
            }
        }
        else{
            if(ls2.contain(ls1.el)){
                return vector<double>{ ls1.el.x, ls1.el.y };
            }
        }
        return vector<double>();
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值