0457. 环形数组是否存在循环
大约 4 分钟
--- 

0457. 环形数组是否存在循环
- 标签:数组、哈希表、双指针
- 难度:中等
题目链接
题目大意
描述:
存在一个不含 的「环形」数组 ,每个 都表示位于下标 的角色应该向前或向后移动的下标个数:
- 如果 是正数,向前(下标递增方向)移动 步。
- 如果 是负数,向后(下标递减方向)移动 步。
因为数组是「环形」的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。
数组中的「循环」由长度为 的下标序列 标识:
- 遵循上述移动规则将导致一组重复下标序列 。
- 所有 应当不是「全正」就是「全负」。
- 。
要求:
如果 中存在循环,返回 ;否则,返回 。
说明:
。
。
。
进阶:你能设计一个时间复杂度为 且额外空间复杂度为 的算法吗?
示例:
- 示例 1:

输入:nums = [2,-1,1,2,2]
输出:true
解释:图片展示了节点间如何连接。白色节点向前跳跃,而红色节点向后跳跃。
我们可以看到存在循环,按下标 0 -> 2 -> 3 -> 0 --> ...,并且其中的所有节点都是白色(以相同方向跳跃)。- 示例 2:

输入:nums = [-1,-2,-3,-4,-5,6]
输出:false
解释:图片展示了节点间如何连接。白色节点向前跳跃,而红色节点向后跳跃。
唯一的循环长度为 1,所以返回 false。解题思路
思路 1:快慢指针检测
- 使用快慢指针(Floyd 判圈算法)来检测循环。设置两个指针 和 ,初始都指向同一个位置。
- 慢指针每次移动 步,快指针每次移动 步。
- 根据数组元素的值计算下一个位置:,如果结果为负数,需要加上 来保证在有效范围内。
- 在每次移动前需要检查:
- 下一个位置的元素符号必须与起始方向相同(全正或全负)。
- 不能出现自环(当前位置的下一个位置就是自己)。
- 如果快慢指针相遇,说明存在有效循环,返回 。
- 对于每个起始位置都进行检测,如果找到一个有效循环就返回 ,否则继续检测下一个位置。
思路 1:代码
class Solution:
def circularArrayLoop(self, nums: List[int]) -> bool:
n = len(nums)
def getNext(cur):
"""计算下一个位置"""
return (cur + nums[cur]) % n
# 遍历每个位置作为起始点
for i in range(n):
# 如果当前位置已经被访问过,跳过
if nums[i] == 0:
continue
# 记录当前路径的方向(true 为正,false 为负)
direction = nums[i] > 0
# 使用快慢指针检测循环
slow = i
fast = i
# 移动快慢指针直到相遇或违反条件
while True:
# 慢指针移动一步前,检查下一个位置的符号和自环
slow_next = getNext(slow)
if (nums[slow_next] > 0) != direction or slow == slow_next:
break
slow = slow_next
# 快指针移动两步,每步都需要检查
fast_next = getNext(fast)
if (nums[fast_next] > 0) != direction or fast == fast_next:
break
fast = fast_next
fast_next = getNext(fast)
if (nums[fast_next] > 0) != direction or fast == fast_next:
break
fast = fast_next
# 如果快慢指针相遇,说明存在有效循环
if slow == fast:
return True
# 将当前路径上的所有元素标记为已访问
# 这样可以避免重复检测
temp = i
while nums[temp] != 0 and (nums[temp] > 0) == direction:
next_pos = getNext(temp)
nums[temp] = 0 # 标记为已访问
temp = next_pos
return False思路 1:复杂度分析
- 时间复杂度:,其中 是数组的长度。每个位置最多被访问一次,总的时间复杂度为 。
- 空间复杂度:,只使用了常数额外空间,没有使用额外的数据结构。