【Leetcode】457. Circular Array Loop

题目地址:

https://leetcode.com/problems/circular-array-loop/

给定一个长 n n n且不含 0 0 0的数组 A A A,每个数 A [ i ] A[i] A[i]表示如果当前位置在 i i i的话,需要做的位移,即如果 A [ i ] > 0 A[i]>0 A[i]>0则要向右移动 A [ i ] A[i] A[i]步,否则要向左移动 A [ i ] A[i] A[i]步。注意要将 A A A视为循环数组,如果走出界了会走到另一边。问是否存在环。环的长度要大于 1 1 1,并且每一步的方向要都相同。要求空间 O ( 1 ) O(1) O(1) A [ i ] ≤ 1000 A[i]\le 1000 A[i]1000

从任一点开始出发,它的下一个位置是确定的,由于 A [ i ] ≠ 0 A[i]\ne 0 A[i]=0,那么从这个点出发走下去一定会走到某个之前走过的点。我们只需要排除一些不符合要求的情况就行了。为此我们要开一个变量存当前走的方向,方向只有两个正向和负向,如果中途发现方向不对,则不符合要求;此外因为题目要求环的长度大于 1 1 1,所以还需要存最后一步走的步数,如果这个步数是 n n n的倍数,说明这是自环,也不符合要求。并且,为了知道是否走到了之前走过的地方,我们需要对走过的数打个标记。因为空间要 O ( 1 ) O(1) O(1),我们考虑直接在数组里打标记,但是当某次循环走到打了标记的数的时候,我们需要知道这个标记是前面几轮打上的,还是本轮打上的(前面轮打上的不作数的,只有本轮打上的才有可能产生环),所以每一轮的标记需要不一样。我们可以每轮的标记是 2000 + p 2000+p 2000+p p p p是出发点下标,这样只有走到了 2000 + p 2000+p 2000+p这个标记才退出循环。代码如下:

class Solution {
 public:
  bool circularArrayLoop(vector<int>& a) {
    int n = a.size();
	// BASE是打标记的初始数字
    const int BASE = 2000;
    for (int i = 0; i < n; i++) a[i] %= n;
    for (int i = 0; i < n; i++) {
      // 大于等于BASE说明之前的某轮走到过i这个位置而没有发现环,所以直接略过;自环也略过
      if (a[i] >= BASE || !a[i]) continue;
      int p = i, mark = BASE + i;
      bool sign = a[p] > 0;
      do {
        int ne = (p + a[p] + n) % n;
        a[p] = mark;
        p = ne;
        // 如果走到了自环,或者走到之前走过的路径,或者方向变了,则退出循环
      } while (a[p] && a[p] < BASE && sign == a[p] > 0);
      // 只有走到了本轮走过的路径,才找到了环
      if (a[p] == mark) return true;
    }

    return false;
  }
};

时间复杂度 O ( n ) O(n) O(n)(因为每个数最多只会打一次标记),空间 O ( 1 ) O(1) O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值