线段求交点

最近做了一道让人抓狂的题目:题目链接:交点
简单地说,题目要求计算两条线段的交点。之所以让人抓狂,是因为相交的情况有很多种,不相交的情况也有很多种,稍微不注意就会遗漏情况,而且本身写起来就比较复杂(四个点八个数值来回计算)。

在开始解决这道题目前先给出一个小结论
对于X轴上的线段 A B AB AB和线段 C D CD CD(点A坐标 ( a , 0 ) (a,0) (a,0),点B坐标 ( b , 0 ) (b,0) (b,0),点C坐标 ( c , 0 ) (c,0) (c,0),点D坐标 ( d , 0 ) (d,0) (d,0) a < b 且 c < d a<b且c<d a<bc<d),二者有交点的充要条件是:
m a x ( a , c ) ≤ m i n ( b , d ) max(a, c) \le min(b,d) max(a,c)min(b,d)
这个结论在这里不给出证明了,大家在纸上画画应该很容易理解。接下来,开始解决线段交点问题!

我们以线段对应的直线是否存在斜率将情况分为3种:1.两条直线都存在斜率、2.只有一条直线存在斜率和3.两条直线都不存在斜率。下面对三种情况分别讨论。

1. 两条直线都存在斜率

这种情况下首先计算出两条直线的斜率和截距k1、b1、k2、b2

平行( k 1 = = k 2 k1==k2 k1==k2

b 1 ! = b 2 b1 != b2 b1!=b2,则二者必然没有交点。

b 1 = = b 2 b1 == b2 b1==b2,说明两条直线共线,我们需要检查两条线段是否重合。如何检查呢?只需要将两条线段向X轴投影,然后用开篇的小结论判断是否有交点即可。
此时线段有多个交点,题目要求返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。可以证明,该点必然是排序(按横坐标升序,若横坐标相同按纵坐标升序)后的第二个点

不平行( k 1 ! = k 2 k1 != k2 k1!=k2

这种情况下直接解出交点坐标,然后判断是否在两条线段的范围内即可。

2. 只有一条直线存在斜率

这种情况下直接解出交点坐标,然后判断是否在两条线段的范围内即可。

3. 两条直线都不存在斜率

这种情况实际和情况1下的平行是类似的,我们只需要将两条线段顶点的横纵坐标交换,然后代入情况1下的平行即可。需要注意的是返回结果时需要再进行一次横纵坐标交换

最后祭出AC code:

class Solution {
public:
    vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
        vector<double> ans;    
        bool isswap = false;  
        // 情况3,直接交换横纵坐标并标记
        if (start1[0] == end1[0] && start2[0] == end2[0]){
            isswap = true;
            swap(start1[0], start1[1]); swap(end1[0], end1[1]);
            swap(start2[0], start2[1]); swap(end2[0], end2[1]);
        }
        // 如果是情况2,通过交换使start1、end1是无斜率的,start2、end2是有斜率的,可以简化代码
        else if (start2[0] == end2[0]){
            swap(start1, start2); swap(end1, end2);
        }
        
        // 情况2
        if (start1[0] == end1[0]){
            double k2 = (double)(end2[1]-start2[1]) / (end2[0]-start2[0]), b2 = start2[1]-k2*start2[0];
            double tx = start1[0], ty = k2 * tx + b2;
            // 判断交点是否在范围内
            if (tx >= min(start2[0], end2[0]) && tx <= max(start2[0], end2[0])){
            	return {tx, ty};
            }

        }
        else{ // 情况1
            double k1 = (double)(end1[1]-start1[1]) / (end1[0]-start1[0]), b1 = start1[1]-k1*start1[0];
            double k2 = (double)(end2[1]-start2[1]) / (end2[0]-start2[0]), b2 = start2[1]-k2*start2[0];
            if (k1 == k2){ //平行
                if (b1 != b2) return {};
                // 判断线段在X轴上的投影是否相交
                int a = min(start1[0], end1[0]), b = max(start1[0], end1[0]);
                int c = min(start2[0], end2[0]), d = max(start2[0], end2[0]);
        	    if (max(a, c) > min(b, d)) return {};
        	    
                vector<vector<int>> arr;
        		arr.push_back(start1); arr.push_back(start2); arr.push_back(end1); arr.push_back(end2);
        		sort(arr.begin(), arr.end());
                vector<double> ret;
                ret.push_back(arr[1][0]); ret.push_back(arr[1][1]);
                if (isswap) swap(ret[0], ret[1]);
                return ret;
            }
            // 不平行
            double tx = (b2-b1) / (k1-k2), ty = k1*tx+b1;
            // 检查检点是否在范围内
            if (tx >= min(start1[0], end1[0]) && tx <= max(start1[0], end1[0]) && tx >= min(start2[0], end2[0]) && tx <= max(start2[0], end2[0])){
                return {tx, ty};
            }
            
        }
        return {};

    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值