力扣每日一题2021-08-07环形数组是否存在循环


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中存在循环,返回trye;否则返回false。


示例1:

输入:

nums = [2, -1, 1, 2, 2]

输出:

true

解释:

存在循环,按下标0->2->3->0->…进入循环。循环长度为3。


示例2

输入:

nums = [-1, 2]

输出:

false

解释:

按下标1->1->…进行循环,由于循环长度为1,但是根据定义,循环的长度必须大于1,所以不构成循环。


示例3:

输入:

nums = [-2, 1, -1, -2, -2]

输出:

false

解释:

按下标1->2->1->…进行循环,但是nums[1]是正数,nums[2]是负数,按照所有nums[seq[j]]应当全正或全负的规则,所以不构成循环,返回false。


思路:快慢指针

快慢指针:

将环形数组理解为图中的n个点,nums[i]表示i号点向[(i+nums[i]) mod n]号点连有一条单向的边。
这张图中每个点有且仅有一条出边,这样从某个点出发,沿着单向边不断移动,最终必然会进入一个环中。根据题意,要检查图中是否存在一个所有单向边方向一致的环。可以采用快慢指针解决本题。
具体而言,检查每个节点,令快指针从当前节点出发,每次移动两步,慢指针每次移动一步。每次移动,都检查方向是否与初始方向一致,若不一致,则结束遍历返回false。
遍历过程中,如果快慢指针相遇,或者移动方向改变,停止遍历。
当nums[i]为n的整数倍,即i的后继结点为i本身时,循环长度k=1,不满足题意,需要跳过此种情况。
对于遍历过的节点,如果仍未成环,则将这些节点对应数组中的值置为0,再继续遍历其他节点,以免重复访问。

Python代码

快慢指针

class Solution:
    def circularArrayLoop(self, nums: List[int]) -> bool:
        n = len(nums)
        # 找到x的下一个位置
        nextnode = lambda x: (x+nums[x]) % n
        for i in range(n):
            if nums[i] == 0:
                continue
            slow = i
            fast = nextnode(i)
            # 判断同向移动
            while nums[slow]*nums[fast] > 0 and nums[fast] * nums[nextnode(fast)] > 0:
                # 如果快慢指针相等,说明有环
                if fast == slow:
                    # 如果是自环,退出循环
                    if slow == nextnode(slow):
                        break
                    else:
                        return True
                # 快指针一次走两步,慢指针一次走一步
                slow = nextnode(slow)
                fast = nextnode(nextnode(fast))
            # 访问过的节点设置为0
            # 如果此前没有return,则此前遍历过的元素都不成环,为避免再次无效查找,将这些节点设置为0
            while nums[i] > 0:
                # 先找到下一个元素的index,将当前元素置为0,向下走
                tmp = nextnode(i)
                nums[i] = 0
                i = tmp
        # 当所有节点都遍历后,未成环返回False
        return False
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值