目录

0457:环形数组是否存在循环(★)

力扣第 457 题

题目

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

  • 如果 nums[i] 是正数,向前(下标递增方向)移动 |nums[i]|
  • 如果 nums[i] 是负数,向后(下标递减方向)移动 |nums[i]|

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

数组中的 循环 由长度为 k 的下标序列 seq 标识:

  • 遵循上述移动规则将导致一组重复下标序列 seq[0] -> seq[1] -> ... -> seq[k - 1] -> seq[0] -> ...
  • 所有 nums[seq[j]] 应当不是 全正 就是 全负
  • k > 1

如果 nums 中存在循环,返回 true ;否则,返回 false

示例 1:

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

示例 2:

输入:nums = [-1,2]
输出:false
解释:按下标 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。

示例 3:

输入:nums = [-2,1,-1,-2,-2]
输出:false
解释:按下标 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为 nums[1] 是正数,而 nums[2] 是负数。
所有 nums[seq[j]] 应当不是全正就是全负。

提示:

  • 1 <= nums.length <= 5000
  • -1000 <= nums[i] <= 1000
  • nums[i] != 0

进阶:你能设计一个时间复杂度为 O(n) 且额外空间复杂度为 O(1) 的算法吗?

分析

  • 考虑遍历每个起点模拟,正负性改变或重复即中止
  • 要求时间 O(N),那么失败的下标应该做标记,防止重复遍历
  • 要求空间 O(1),因此考虑直接在数组上做标记
  • 之前失败的标记和当前正在模拟经过的标记应该是不同的
  • 有个想法是用起点下标 i 加上一个大值(本题大于1000即可)作为标记,即可区分

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    def circularArrayLoop(self, nums: List[int]) -> bool:
        def check(i):
            if nums[i]>ma:
                return False
            M = i+ma+1
            while True:
                j = (i+nums[i])%n
                if nums[j]>ma:
                    return nums[j]==M
                if j==i or nums[i]*nums[j]<0:
                    return False
                nums[i] = M
                i = j

        ma = 1000
        n = len(nums)
        return any(check(i) for i in range(n))

38 ms