zoukankan      html  css  js  c++  java
  • 面试题 16.03. 交点

    面试题 16.03. 交点


    给定两条线段(表示为起点start = {X1, Y1}和终点end = {X2, Y2}),如果它们有交点,请计算其交点,没有交点则返回空值。
    要求浮点型误差不超过10^-6。若有多个交点(线段重叠)则返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。

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

    来源:https://leetcode-cn.com/problems/intersection-lcci/
    题解:

    1. 排除界限不重合的情况
    if (Math.max(a[0], b[0]) < Math.min(c[0], d[0]) ||
        Math.max(c[0], d[0]) < Math.min(a[0], b[0]) ||
        Math.max(a[1], b[1]) < Math.min(c[1], d[1]) ||
        Math.max(c[1], d[1]) < Math.min(a[1], b[1])) {
        return [];
    }
    
    1. 根据叉乘的正负判断两个点是否在一条线段的两侧,原理见下图
      相交
      不相交
    /* 获取向量叉乘值 sx x sy */
    function getCross(s, x, y) {
        let x1, y1, x2, y2;
        [x1, y1] = [x[0] - s[0], x[1] - s[1]];
        [x2, y2] = [y[0] - s[0], y[1] - s[1]];
        return x1 * y2 - x2 * y1;
    }
    let acb = getCross(a, c, b);//ac x ab
    let adb = getCross(a, d, b);//ad x ab
    let cad = getCross(c, a, d);//ca x cd
    let cbd = getCross(c, b, d);//cb x cd
    
    1. 确保有交点后根据y=kx+b根据数学公式获取两条线段的k和b,提前排序是为了垂直时后面可以直接取到最小值
    /* 统一端点顺序 */
    if (a[0] > b[0] || a[0] == b[0] && a[1] > b[1]) [a, b] = [b, a];
    if (c[0] > d[0] || c[0] == d[0] && c[1] > d[1]) [c, d] = [d, c];
    /* k = (y2 - y1) / (x2 - x1) */
    let k1 = (b[1] - a[1]) / (b[0] - a[0]);
    let k2 = (d[1] - c[1]) / (d[0] - c[0]);
    /* b = (x2 * y1 - x1 * y2) / (x2 - x1) */
    let b1 = (b[0] * a[1] - a[0] * b[1]) / (b[0] - a[0]);
    let b2 = (d[0] * c[1] - c[0] * d[1]) / (d[0] - c[0]);
    
    1. 对于共线情况,遍历4个点,先判断是否是公共点,再根据题目要求选x最小的或x相等y最小的
    /* 判断斜率相同情况下x,y是否在两条线段上 */
    function isInLine(x, y) {
        for (let [s, e] of [[a, b], [c, d]]) {
            /* 求边界值 */
            let [maxx, maxy] = [Math.max(s[0], e[0]), Math.max(s[1], e[1])];
            let [minx, miny] = [Math.min(s[0], e[0]), Math.min(s[1], e[1])];
            if (x < minx || x > maxx || y < miny || y > maxy)
                return false;
        }
        return true;
    }
    /* 共线 包括垂直的情况 */
    if (k1 == k2) {
        let x = null, y = null;
        for (let [xx, yy] of [a, b, c, d]) {
            if (isInLine(xx, yy)) {
                if (x == null && y == null ||
                    xx < x || xx == x && yy < y) {
                    x = xx; y = yy;
                }
            }
        }
        return [x, y];
    }
    
    1. 若只有一条线垂直,直接取垂直线的横坐标,计算y值,若没有垂直的线,带公式求x,y即可
    else if (k1 === Infinity) {//ab垂直
        return [a[0], k2 * a[0] + b2];
    }
    else if (k2 === Infinity) {//cd垂直
        return [c[0], k1 * c[0] + b1];
    }
    else {//正常相交情况
        let x = (b2 - b1) / (k1 - k2);
        return [x, x * k1 + b1];
    }
    
    1. 完整代码
    var intersection = function (a, b, c, d) {
        /* 排除区间不相交的情况 */
        if (Math.max(a[0], b[0]) < Math.min(c[0], d[0]) ||
            Math.max(c[0], d[0]) < Math.min(a[0], b[0]) ||
            Math.max(a[1], b[1]) < Math.min(c[1], d[1]) ||
            Math.max(c[1], d[1]) < Math.min(a[1], b[1])) {
            return [];
        }
        /* 获取向量叉乘值 sx x sy */
        function getCross(s, x, y) {
            let x1, y1, x2, y2;
            [x1, y1] = [x[0] - s[0], x[1] - s[1]];
            [x2, y2] = [y[0] - s[0], y[1] - s[1]];
            return x1 * y2 - x2 * y1;
        }
        let acb = getCross(a, c, b);//ac x ab
        let adb = getCross(a, d, b);//ad x ab
        let cad = getCross(c, a, d);//ca x cd
        let cbd = getCross(c, b, d);//cb x cd
        /*有交点 b,c在ab两侧 ab在cd两侧*/
        if (acb * adb <= 0 && cad * cbd <= 0) {
            /* 统一端点顺序 */
            if (a[0] > b[0] || a[0] == b[0] && a[1] > b[1]) [a, b] = [b, a];
            if (c[0] > d[0] || c[0] == d[0] && c[1] > d[1]) [c, d] = [d, c];
            /* k = (y2 - y1) / (x2 - x1) */
            let k1 = (b[1] - a[1]) / (b[0] - a[0]);
            let k2 = (d[1] - c[1]) / (d[0] - c[0]);
            /* b = (x2 * y1 - x1 * y2) / (x2 - x1) */
            let b1 = (b[0] * a[1] - a[0] * b[1]) / (b[0] - a[0]);
            let b2 = (d[0] * c[1] - c[0] * d[1]) / (d[0] - c[0]);
            /* 判断斜率相同情况下x,y是否在两条线段上 */
            function isInLine(x, y) {
                for (let [s, e] of [[a, b], [c, d]]) {
                    /* 求边界值 */
                    let [maxx, maxy] = [Math.max(s[0], e[0]), Math.max(s[1], e[1])];
                    let [minx, miny] = [Math.min(s[0], e[0]), Math.min(s[1], e[1])];
                    if (x < minx || x > maxx || y < miny || y > maxy)
                        return false;
                }
                return true;
            }
            /* 共线 包括垂直的情况 */
            if (k1 == k2) {
                let x = null, y = null;
                for (let [xx, yy] of [a, b, c, d]) {
                    if (isInLine(xx, yy)) {
                        if (x == null && y == null ||
                            xx < x || xx == x && yy < y) {
                            x = xx; y = yy;
                        }
                    }
                }
                return [x, y];
            }
            else if (k1 === Infinity) {//ab垂直
                return [a[0], k2 * a[0] + b2];
            }
            else if (k2 === Infinity) {//cd垂直
                return [c[0], k1 * c[0] + b1];
            }
            else {//正常相交情况
                let x = (b2 - b1) / (k1 - k2);
                return [x, x * k1 + b1];
            }
        }
        /* 没有交点 */
        return [];
    };
    
  • 相关阅读:
    HTTP请求方法 GET POST【总结】
    Data Binding MVVM 数据绑定 总结
    Butter Knife 黄油刀
    原生JS强大DOM选择器querySelector与querySelectorAll
    JS日期Date详解与实例扩展
    JS中的冒泡排序以及实现一个数组中得最到最大的数字小例
    jquery.roundabout.js实现3D图片层叠旋转木马切换
    AngularJS 表格
    AngularJS XMLHttpRequest
    AngularJS Filters
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/12990185.html
Copyright © 2011-2022 走看看