线线关系

线线关系

问题描述

线线关系处理平面上两条直线/线段之间的基本关系,包括相交判断、交点计算等。常用于几何判定和相交问题。

基本操作

算法思想

  1. 使用向量表示直线方向
  2. 基于叉积判断相交
  3. 适用于相交和平行判断
  4. 时间复杂度

代码实现

class Solution {
public:
    // 判断两线段相交
    bool segmentIntersect(const Line& l1, const Line& l2) {
        Point a = l1.p1, b = l1.p2, c = l2.p1, d = l2.p2;
        
        // 快速排斥实验
        if (max(a.x, b.x) < min(c.x, d.x) ||
            max(c.x, d.x) < min(a.x, b.x) ||
            max(a.y, b.y) < min(c.y, d.y) ||
            max(c.y, d.y) < min(a.y, b.y)) {
            return false;
        }
        
        // 跨立实验
        double d1 = (b - a).cross(c - a);
        double d2 = (b - a).cross(d - a);
        double d3 = (d - c).cross(a - c);
        double d4 = (d - c).cross(b - c);
        
        return d1 * d2 <= 0 && d3 * d4 <= 0;
    }
    
    // 计算两直线交点
    Point lineIntersection(const Line& l1, const Line& l2) {
        Point a = l1.p1, b = l1.p2, c = l2.p1, d = l2.p2;
        double s1 = (b - a).cross(c - a);
        double s2 = (b - a).cross(d - a);
        return Point(
            (c.x * s2 - d.x * s1) / (s2 - s1),
            (c.y * s2 - d.y * s1) / (s2 - s1)
        );
    }
    
    // 判断两直线平行
    bool isParallel(const Line& l1, const Line& l2) {
        Point dir1 = l1.p2 - l1.p1;
        Point dir2 = l2.p2 - l2.p1;
        return abs(dir1.cross(dir2)) < EPS;
    }
};
class Solution {
    static final double EPS = 1e-10;
    
    // 判断两线段相交
    public boolean segmentIntersect(Line l1, Line l2) {
        Point a = l1.p1, b = l1.p2, c = l2.p1, d = l2.p2;
        
        // 快速排斥实验
        if (Math.max(a.x, b.x) < Math.min(c.x, d.x) ||
            Math.max(c.x, d.x) < Math.min(a.x, b.x) ||
            Math.max(a.y, b.y) < Math.min(c.y, d.y) ||
            Math.max(c.y, d.y) < Math.min(a.y, b.y)) {
            return false;
        }
        
        // 跨立实验
        double d1 = b.subtract(a).cross(c.subtract(a));
        double d2 = b.subtract(a).cross(d.subtract(a));
        double d3 = d.subtract(c).cross(a.subtract(c));
        double d4 = d.subtract(c).cross(b.subtract(c));
        
        return d1 * d2 <= 0 && d3 * d4 <= 0;
    }
    
    // 计算两直线交点
    public Point lineIntersection(Line l1, Line l2) {
        Point a = l1.p1, b = l1.p2, c = l2.p1, d = l2.p2;
        double s1 = b.subtract(a).cross(c.subtract(a));
        double s2 = b.subtract(a).cross(d.subtract(a));
        return new Point(
            (c.x * s2 - d.x * s1) / (s2 - s1),
            (c.y * s2 - d.y * s1) / (s2 - s1)
        );
    }
    
    // 判断两直线平行
    public boolean isParallel(Line l1, Line l2) {
        Point dir1 = l1.p2.subtract(l1.p1);
        Point dir2 = l2.p2.subtract(l2.p1);
        return Math.abs(dir1.cross(dir2)) < EPS;
    }
}
class Solution:
    def __init__(self):
        self.EPS = 1e-10
    
    # 判断两线段相交
    def segment_intersect(self, l1: Line, l2: Line) -> bool:
        a, b = l1.p1, l1.p2
        c, d = l2.p1, l2.p2
        
        # 快速排斥实验
        if (max(a.x, b.x) < min(c.x, d.x) or
            max(c.x, d.x) < min(a.x, b.x) or
            max(a.y, b.y) < min(c.y, d.y) or
            max(c.y, d.y) < min(a.y, b.y)):
            return False
        
        # 跨立实验
        d1 = (b - a).cross(c - a)
        d2 = (b - a).cross(d - a)
        d3 = (d - c).cross(a - c)
        d4 = (d - c).cross(b - c)
        
        return d1 * d2 <= 0 and d3 * d4 <= 0
    
    # 计算两直线交点
    def line_intersection(self, l1: Line, l2: Line) -> Point:
        a, b = l1.p1, l1.p2
        c, d = l2.p1, l2.p2
        s1 = (b - a).cross(c - a)
        s2 = (b - a).cross(d - a)
        return Point(
            (c.x * s2 - d.x * s1) / (s2 - s1),
            (c.y * s2 - d.y * s1) / (s2 - s1)
        )
    
    # 判断两直线平行
    def is_parallel(self, l1: Line, l2: Line) -> bool:
        dir1 = l1.p2 - l1.p1
        dir2 = l2.p2 - l2.p1
        return abs(dir1.cross(dir2)) < self.EPS

时间复杂度分析

操作 时间复杂度 空间复杂度
线段相交判断
直线交点计算
平行判断
重合判断

应用场景

  1. 相交判断
  2. 交点计算
  3. 平行判断
  4. 重合判断
  5. 几何构造

注意事项

  1. 浮点数精度
  2. 除零判断
  3. 边界情况
  4. 平行处理
  5. 重合处理

常见变形

  1. 多线段相交
class Solution {
public:
    // 判断多条线段是否存在相交
    bool hasIntersection(vector<Line>& segments) {
        int n = segments.size();
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (segmentIntersect(segments[i], segments[j])) {
                    return true;
                }
            }
        }
        return false;
    }
    
    // 计算所有相交点
    vector<Point> findAllIntersections(vector<Line>& segments) {
        vector<Point> intersections;
        int n = segments.size();
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (segmentIntersect(segments[i], segments[j])) {
                    intersections.push_back(
                        lineIntersection(segments[i], segments[j])
                    );
                }
            }
        }
        return intersections;
    }
};
class Solution {
    // 判断多条线段是否存在相交
    public boolean hasIntersection(Line[] segments) {
        int n = segments.length;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (segmentIntersect(segments[i], segments[j])) {
                    return true;
                }
            }
        }
        return false;
    }
    
    // 计算所有相交点
    public List<Point> findAllIntersections(Line[] segments) {
        List<Point> intersections = new ArrayList<>();
        int n = segments.length;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (segmentIntersect(segments[i], segments[j])) {
                    intersections.add(
                        lineIntersection(segments[i], segments[j])
                    );
                }
            }
        }
        return intersections;
    }
}
class Solution:
    # 判断多条线段是否存在相交
    def has_intersection(self, segments: List[Line]) -> bool:
        n = len(segments)
        for i in range(n):
            for j in range(i + 1, n):
                if self.segment_intersect(segments[i], segments[j]):
                    return True
        return False
    
    # 计算所有相交点
    def find_all_intersections(self, segments: List[Line]) -> List[Point]:
        intersections = []
        n = len(segments)
        for i in range(n):
            for j in range(i + 1, n):
                if self.segment_intersect(segments[i], segments[j]):
                    intersections.append(
                        self.line_intersection(segments[i], segments[j])
                    )
        return intersections
  1. 线段集合处理
class Solution {
public:
    // 计算线段集合的总长度(处理重叠)
    double totalLength(vector<Line>& segments) {
        // 按左端点排序
        sort(segments.begin(), segments.end(),
             [](const Line& a, const Line& b) {
                 return a.p1.x < b.p1.x;
             });
        
        double total = 0;
        double right = -DBL_MAX;
        
        for (const Line& seg : segments) {
            if (seg.p1.x > right) {
                total += seg.p2.x - seg.p1.x;
            } else if (seg.p2.x > right) {
                total += seg.p2.x - right;
            }
            right = max(right, seg.p2.x);
        }
        
        return total;
    }
};
class Solution {
    // 计算线段集合的总长度(处理重叠)
    public double totalLength(Line[] segments) {
        // 按左端点排序
        Arrays.sort(segments, (a, b) -> Double.compare(a.p1.x, b.p1.x));
        
        double total = 0;
        double right = Double.NEGATIVE_INFINITY;
        
        for (Line seg : segments) {
            if (seg.p1.x > right) {
                total += seg.p2.x - seg.p1.x;
            } else if (seg.p2.x > right) {
                total += seg.p2.x - right;
            }
            right = Math.max(right, seg.p2.x);
        }
        
        return total;
    }
}
class Solution:
    # 计算线段集合的总长度(处理重叠)
    def total_length(self, segments: List[Line]) -> float:
        # 按左端点排序
        segments.sort(key=lambda seg: seg.p1.x)
        
        total = 0
        right = float('-inf')
        
        for seg in segments:
            if seg.p1.x > right:
                total += seg.p2.x - seg.p1.x
            elif seg.p2.x > right:
                total += seg.p2.x - right
            right = max(right, seg.p2.x)
        
        return total
牛客代码笔记-牛栋 文章被收录于专栏

汗牛充栋,学海无涯。<br/> 内含算法知识点讲解,以及牛客题库精选例题。<br/> 学习算法,从牛栋开始。

全部评论

相关推荐

10-19 10:28
已编辑
西南石油大学 后端工程师
团孝子已上线feeling:面了很多家公司,能感受到目前只有小公司+外包喜欢问八股。大厂虽然也问八股,但是是从实习、项目中进行提问,并且大厂会问很深,面试官也会对你的回答进行思考➕追问,所以准备大厂面试前一定要备好相关资料。对于算法,我做的是codetop前100+力扣hot100+力扣高频150,面试中实感hot100就足够,基本上只要是hot100就秒答。对于项目和八股,我做的也是烂大街的星球项目,八股则是看小林和问ai,自己也写了很多技术博客和画了很多思维导图,并且自己也尝试用嘴巴说出来,不只停留于纸面。运气也很重要,必须要让面试官/HR看到简历才行,所以建议投递时间是下午两点。tl:第一岗位9.9&nbsp;投递9.10&nbsp;一面(一面评价:最近见过最强的大三,结束五分钟后约二面,都晚上九点了不下班吗)9.11&nbsp;二面(三道算法a出两道,反问评价:经验不够等横向,我实习生要啥经验)9.21挂(实习时间过短+其他原因,想要一年实习的,为什么不招个正职)第二岗位10.10投递10.11约面(主管打电话,说看到我之前投递记录了想要我挂qa职进去干后端,同意)10.14&nbsp;一面(无八股,主动说确实很强,意愿很强)10.16&nbsp;oc其余,友邦,东软,东华,惠择,用友oc已拒京东测开一面挂(投后端被测开捞)腾讯测试已拒(投后端被测开捞)ps:表扬惠择的主管面,没怎么问技术(可能是一面面试官沟通过了),全程一起讲大道理,解答了心中很多疑惑,也告诉我以面试官角度来看怎么选候选人,如果可以下次一定选惠择
HeaoDng:美团好像可以触发一面通
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务