给定一个含有 n 个正数的数组 x。从点 (0,0) 开始,先向北移动 x[0] 米,然后向西移动 x[1] 米,向南移动 x[2] 米,向东移动 x[3] 米,持续移动。也就是说,每次移动后你的方位会发生逆时针变化。
编写一个 O(1) 空间复杂度的一趟扫描算法,判断你所经过的路径是否相交。
示例 1:
┌───┐
│ │
└───┼──>
│
输入: [2,1,1,2]
输出: true
示例 2:
┌──────┐
│ │
│
│
└────────────>
输入: [1,2,3,4]
输出: false
示例 3:
┌───┐
│ │
└───┼>
输入: [1,1,1,1]
输出: true
首先分析问题,有以下几种情况:
当线段条数小于 4 时,不可能发生交叉。
线段条数大于等于 4 时,开始出现交叉的可能性:
第 4 条线段只可能与第 1 条线段交叉。
第 5 条线段只可能与第 2、1 条线段交叉。
第 6 条线段只可能与第 3、(2)、1 条线段交叉。
第 7 条线段只可能与第 4、(3)、2 条线段交叉(注意,不可能与第 1 条线段交叉,因为路径是逆时针的,此时线段 1 要么被围在内部,要么被抛在外部)
……
从上面分析可以看出,我们至多需要一个大小为 6 的滑动窗口,即可覆盖所有的交叉情况。滑动窗口每移动一步,我们都判断一次新进入窗口的线段与最前面三条线段的交叉关系。
一个边界情况是,前 4 条、前 5 条线段虽不够 6 个窗格,但却有可能发生交叉,同样需要判断。我们只需要在输入数组的开头补上 2 个 0 就能解决这个问题。但考虑到数组插入是一个比较耗时的操作,我们只需要从逻辑上模拟这个过程即可。
class Solution {
public boolean isSelfCrossing(int[] x) {
if(x.length<4) return false;
int a=0,b=0,c=0;
int d=x[0],e=x[1],f=x[2];
for(int i=3;i<x.length;i++)
{
a=b;b=c;c=d;d=e;e=f;f=x[i];
if(e<c-a && f>=d) return true;
if(c-a<=e && e<=c && f>=(d-b<0?d:d-b)) return true;
}
return false;
}
}