題目來自 leetcode 335. Self Crossing。git
題意很是簡單,有一個點,一開始位於 (0, 0) 位置,而後有規律地往上,左,下,右方向移動必定的距離,判斷是否會相交(self crossing)。github
一個很容易想到的方案就是求出全部線段,而後用 O(n^2) 的時間複雜度兩兩判斷線段是否相交,而線段相交的判斷,能夠列個二元一次方程求解(交點)。這個解法很是容易想到,可是實際操做起來比較複雜,接下去介紹利用向量的解法。3d
簡單回顧下向量,具體的自行谷歌。向量就是一條有向線段,這裏要用到的是向量的 叉乘。(還有個相似的概念叫作 點積)code
叉乘公式:blog
而由於 p1 X p2 = | a | * | b | * sin α,後者又是平行四邊形的面積公式,因此能夠用叉乘來求解平行四邊形的面積(同理能夠求解三角形的面積)。PS:若是給出三個點座標,求解三角形面積的話,最好用叉乘來作,這樣更精確,而不是海倫公式。leetcode
向量叉乘還能判斷 p0p1 和 p0p2 兩個向量, 對於 p0 點而言,p0p1 是位於 p0p2 順時針方向,仍是逆時針方向。get
咱們回到判斷線段相交,兩線段相交有以下兩種可能。it
對於第一種狀況,咱們能夠判斷 p1, p2 兩點分別位於線段 p3p4 兩邊,同時知足 p3, p4 兩點分別位於線段 p1p2 兩邊。如何判斷?以判斷 p1,p2 是否位於線段 p3p4 兩邊爲例,求解叉乘 p3p1 X p3p4,以及 p3p2 X p3p4,若是符合條件,兩值必是一正一負。對於第二種狀況,由於共線,因此叉乘結果爲 0,可是叉乘結果爲 0 只能保證共線,並不能保證相交,因此還須要進行以下的判斷。io
這樣解法就呼之欲出了,貢獻個判斷線段相交的模板:ast
/** * @param {object} a * @param {object} b * @return {boolean} * a, b 表示兩條線段。 * (a.x1, a.y1), (a.x2, a.y2) 分別表示線段 a 兩個端點; b 相似 */ function f(a, b) { function online(a, b, c) { if (a.x >= Math.min(b.x, c.x) && a.x <= Math.max(b.x, c.x) && a.y >= Math.min(b.y, c.y) && a.y <= Math.max(b.y, c.y)) return true; return false; } var n1, n2, n3, n4; n1 = (a.x1 - b.x2) * (b.y1 - b.y2) - (a.y1 - b.y2) * (b.x1 - b.x2); n2 = (a.x2 - b.x2) * (b.y1 - b.y2) - (a.y2 - b.y2) * (b.x1 - b.x2); n3 = (b.x1 - a.x2) * (a.y1 - a.y2) - (b.y1 - a.y2) * (a.x1 - a.x2); n4 = (b.x2 - a.x2) * (a.y1 - a.y2) - (b.y2 - a.y2) * (a.x1 - a.x2); if (n1 * n2 < 0 && n3 * n4 < 0) return 1; var p1 = {x: a.x1, y: a.y1}; var p2 = {x: a.x2, y: a.y2}; var p3 = {x: b.x1, y: b.y1}; var p4 = {x: b.x2, y: b.y2}; if (n1 === 0 && online(p1, p3, p4)) return 1; if (n2 === 0 && online(p2, p3, p4)) return 1; if (n3 === 0 && online(p3, p1, p2)) return 1; if (n4 === 0 && online(p4, p1, p2)) return 1; return 0; }
本題完整代碼詳見 https://github.com/hanzichi/leetcode/tree/master/Algorithms/Self%20Crossing,求 star, 求 fork,求 follow