问题
存在一个不含 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)$。