[LeetCode] 335. Self Crossing

You are given an array x of n positive numbers. You start at point (0,0) and moves x[0] metres to the north, then x[1] metres to the west, x[2] metres to the south, x[3]metres to the east and so on. In other words, after each move your direction changes counter-clockwise.

Write a one-pass algorithm with O(1) extra space to determine, if your path crosses itself, or not.

Example 1:

Given x = [2, 1, 1, 2],
┌───┐
│   │
└───┼──>
    │

Return true (self crossing)

Example 2:

Given x = [1, 2, 3, 4],
┌──────┐
│      │
│
│
└────────────>

Return false (not self crossing)

Example 3:

Given x = [1, 1, 1, 1],
┌───┐
│   │
└───┼>

Return true (self crossing)


Tips:

题目中对额外的使用空间进行了限制,

这里不加证明的给出:若按顺序给各条线段编号,有线段相交,则必有相交线段的编号差值不超过6,(即若线段8为最后一条线段且发生了相交,则相交的线段编号必大于2,这一点我证明不出来,可以自己画一下,不可能有差值超过6的情况,实际上,编号为n的线段只可能与编号为n-3,n-4,n-5的线段相交,n-i>0,后面会用到)。

这样的话,只需要保存后7个点的信息,具体算法为:

1)计算各点的坐标,并保存后7个点;

2)计算线段 n 和 n-i 的端点信息;

3)判断两线段是否相交;

4)重复步骤1)2) 3),直到返回true或输入结束。


判断两线段是否相交的方法:

1)两线段交叉:线段1的两端点记为l11和l12,同理线段2的两端点记为l21和l22,计算l11和l21横坐标的差值及l12和l22横坐标的差值,想乘记为dif1,同理计算纵坐标的差值记为dif2,若dif1和dif2均小于0,则相交;

2)最后一线段的末端点在另一线段上(不共端点):dif1和dif2其中一个小于0,另一个等于0;

3)共端点:直接判断是否有端点相同。


bool isSelfCrossing(vector<int>& x) {
    int pos[7][2]; //记录7个点的坐标
    int line[2][2][2]; //待判断的两条线的端点坐标
    memset(pos, 0, sizeof(pos));
 
    int NS = 0, EW = 0; //坐标
    int cnt = 0, k;
    int posi = 0;
 
    for (auto &a : x){ //计算坐标
        k = cnt % 4;
 
        switch (k){
        case 0:
            NS += a;
            break;
        case 1:
            EW -= a;
            break;
        case 2:
            NS -= a;
            break;
        case 3:
            EW += a;
            break;
        }
        ++cnt;
        posi = cnt % 7;
        pos[posi][0] = EW;
        pos[posi][1] = NS;
 
        int tmp, m = 0;
        int dif1, dif2;
        if (cnt == 4) m = 3;
        else if (cnt == 5) m = 4;
        else if (cnt >= 6) m = 5;
        for (int i = 3; i <= m; ++i){ //找出待判断的两条线并判断
            tmp = posi;
            line[0][0][0] = pos[tmp][0];
            line[0][0][1] = pos[tmp][1];
 
            tmp = (posi + 6) % 7;
            line[0][1][0] = pos[tmp][0];
            line[0][1][1] = pos[tmp][1];
 
            tmp = (posi + 7 - i) % 7;
            line[1][0][0] = pos[tmp][0];
            line[1][0][1] = pos[tmp][1];
 
            tmp = (posi + 6 - i) % 7;
            line[1][1][0] = pos[tmp][0];
            line[1][1][1] = pos[tmp][1];
 
            dif1 = (line[0][0][0] - line[1][0][0])*(line[0][1][0] - line[1][1][0]);
            dif2 = (line[0][0][1] - line[1][0][1])*(line[0][1][1] - line[1][1][1]);
            if ((dif1 < 0 && dif2 < 0) || (dif1 == 0 && dif2 < 0) || (dif1 < 0 && dif2 == 0) ||
                (line[0][0][0] == line[1][1][0] && line[0][0][1] == line[1][1][1]))
                return true;
        }
    }
    return false;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值