457. 环形数组是否存在循环

题目要求

  • 找环,长度大于 1,排除自环;全正或全负,即只能沿着一个方向走。。

457. 环形数组是否存在循环_链表

方法一:快慢指针

将环形数组理解为图中的 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