457. 环形数组是否存在循环
题目要求
- 找环,长度大于 1,排除自环;全正或全负,即只能沿着一个方向走。。
方法一:快慢指针
将环形数组理解为图中的 n 个点,nums[i] 表示 i 号点向 (i + nums[i]) mod n 号点连有一条单向边。
图中的每个点有且仅有一条出边,从某一个点出发,沿着单向边不断移动,最终必然会进入一个环中。而依据题目要求,检查图中是否存在一个所有单向边方向一致的环。可以使用在无向图中找环的一个经典算法:快慢指针来解决本题,参考题解「141. 环形链表」。
具体地,检查每一个节点,令快慢指针从当前点出发,快指针每次移动两步,慢指针每次移动一步,期间每移动一次,都需要检查当前单向边的方向是否与初始方向是否一致,如果不一致,即可停止遍历,因为当前路径必然不满足条件。为了降低时间复杂度,可以标记每一个点是否访问过,过程中如果下一个节点为已经访问过的节点,则可以停止遍历。
在实际代码中,无需新建一个数组记录每个点的访问情况,而只需要将原数组的对应元素置零即可(题目保证原数组中元素不为零)。遍历过程中,如果快慢指针相遇,或者移动方向改变,那么我们就停止遍历,并将快慢指针经过的点均置零即可。
特别地,当 nums[i] 为 n 的整倍数时,i 的后继节点即为 i 本身,此时循环长度 k = 1,不符合题目要求,因此我们需要跳过这种情况。
class Solution:
def circularArrayLoop(self, nums: List[int]) -> bool:
N, self.nums = len(nums), nums
for i in range(N):
slow = i
fast = self.nextpos(slow)
while nums[fast] * nums[i] > 0 and nums[self.nextpos(fast)] * nums[i] > 0:
if fast == slow:
if slow == self.nextpos(slow):
break
return True
slow = self.nextpos(slow)
fast = self.nextpos(self.nextpos(fast))
return False
def nextpos(self, index):
N = len(self.nums)
return (index + self.nums[index] + N) % N