【LeetCode-457】环形数组是否存在循环

问题

存在一个不含 0 的 环形 数组 nums ,每个 nums[i] 都表示位于下标 i 的角色应该向前或向后移动的下标个数:

  • 如果 nums[i] 是正数,向前 移动 nums[i] 步
  • 如果 nums[i] 是负数,向后 移动 nums[i] 步

因为数组是 环形 的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。

提示:

  • 所有 nums[seq[j]] 应当不是 全正 就是 全负
  • 循环长度>1
  • 如果 nums 中存在循环,返回 true ;否则,返回 false 。

示例

输入: nums = [2,-1,1,2,2]
输出: true
解释: 存在循环,按下标 0 -> 2 -> 3 -> 0 。循环长度为 3 。

解答

class Solution {
public:
    bool circularArrayLoop(vector<int>& nums) {
        int n = nums.size(), OFFSET = 100000;
        for (int i = 0; i < n; i++) {
            int cur = i, tag = i + OFFSET;
            while (nums[cur] < OFFSET) {
                int next = ((cur + nums[cur]) % n + n) % n;
                if (next == cur) break;
                if (nums[next] == tag) return true;
                if (nums[next] * nums[cur] < 0) break;
                nums[cur] = tag;
                cur = next;
            }
        }
        return false;
    }
};

重点思路

本题类似链表找环的问题(环代表循环数组),可以修改原始数组,所以不需要使用快慢指针。

一个数组中可能会存在多个链表,所以我们需要遍历整个数组。但是可能会遇到之前遍历过的链表中的某一个节点,所以我们每经过一个节点,都对其添加一个OFFSET,这个OFFSET保证不会重复遍历,同时还作为到环入口的标志。时间复杂度\(O(n)\),空间复杂度\(O(1)\)。

拓展

如果不让修改原数组,并且要保证空间复杂度为\(O(1)\)应该怎么做?

代码

class Solution {
public:
    bool circularArrayLoop(vector<int>& nums) {
        int n = nums.size();
        auto next = [&](int cur) {return ((cur + nums[cur]) % n + n) % n;};
        auto same_op = [&](int x, int y) {return (nums[x] * nums[y]) > 0;};
        for (int i = 0; i < n; i++) {
            if (!nums[i]) continue;
            int slow = i, fast = i;
            while (same_op(slow, fast) && same_op(slow, next(fast))) {
                slow = next(slow);
                fast = next(next(fast));
                if (slow == fast) {
                    if (slow != next(slow)) return true; // 保证循环长度>1
                    else break;
                }
            }
        }
        return false;
    }
};

### 重点思路
变成链表找环问题了,因为要便利整个数组,时间复杂度为$O(n^2)$。
上一篇:【LeetCode】287. 寻找重复数【快慢指针】


下一篇:C++——双指针 (转)