LeeCode457-环形数组循环

今天有些偷懒了

一天就做了两道算法题

英语没看

专业知识没复习

可能就得从上午的那篇论文被拒开始的吧


题目描述:

给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。

确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。

示例 1:

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

示例 2:

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

示例 3:

输入:[-2,1,-1,-2,-2]
输出:false
解释:按索引 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为按索引 1 -> 2 的运动是向前的运动,
      而按索引 2 -> 1 的运动是向后的运动。一个循环中的所有运动都必须沿着同一方向行。

提示:

1. -1000 ≤ nums[i] ≤ 1000
nums[i] ≠ 0
0 ≤ nums.length ≤ 5000

思路解析:

这题的话讲真,我刚开始真没多少思路。后来能想到的也就是暴力法吧,无非就是第一层遍历nums数组,选定一个元素后,对该元素进行“跳棋”操作。这里面就有几点需要注意:

1. 一个循环中的所有运动都必须沿着同一方向进行
2. 循环必须在相同的索引处开始和结束并且循环长度 > 1

为了解决条件一的同向问题,我是设定了route集合,只要当前元素的值大于0,就往route集合里加1,反之加-1。如果发现全1集合里出现了-1,或者全-1集合里出现了1,那就立即退出。为了解决条件二,我是设定了index集合,每次添加当前元素的下标,只要当前元素对应的下标出现在index集合内,并且不与index集合内最后一个元素相同,说明nums 中是存在循环,立即退出。

代码如下:

class Solution(object):
    def circularArrayLoop(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        if len(nums) <= 1:
            return False
        flag = False
        for start in range(len(nums)):
            route = []
            indexs = []
            while len(route) <= len(nums) + 1:
                indexs.append(start)
                if nums[start] > 0:
                    route.append(1)
                    if sum(route) != len(route):
                        flag = False
                        break
                else:
                    route.append(-1)
                    if sum(route) != -1 * len(route):
                        flag = False
                        break
                start = (nums[start] + start + len(nums)) % len(nums)
                if start == indexs[-1]:
                    flag = False
                    break
                if start in indexs:
                    flag = True
                    break
            if flag is True:
                return True
        return False


if __name__ == "__main__":
    nums = [-1, 2]
    result = Solution().circularArrayLoop(nums)
    print(result)

但是暴力法,你懂得,一般都是超出时间限制了。

后来,就在网上找到了改进的方法。该方法的核心就是快慢指针法,快慢指针的思想是慢指针走一步,快指针走两步,若他俩相遇肯定是在环中的某一个节点上相遇,则证明存在环。有了这个,那就可以把我们的方法在做一些修改。

代码如下:

class Solution(object):
    def circularArrayLoop(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        def getNext(i):
            return (nums[i]+i)%N

        N = len(nums)
        for i in range(N):
            if nums[i]==0: continue 
            slow = i # 慢指针代表当前位置
            fast = getNext(i) # 快指针代表下一个位置
            # 快慢指针同向且慢指针和快指针下一个也要同向
            while(nums[slow]*nums[fast]>0 and nums[slow]*nums[getNext(fast)]>0):
                if slow==fast: # 快慢指针相遇
                    if slow==getNext(slow):
                        break # 循环长度为1的情况
                    return True
                slow = getNext(slow)
                fast = getNext(getNext(fast))
        return False


if __name__ == "__main__":
    nums = [-1, 2]
    result = Solution().circularArrayLoop(nums)
    print(result)

执行效率也还不错,在500ms左右。毕竟没有用到O(n)的时间复杂度,但是这个也真的想不出来,各位要是想到了还烦请告知在下。

你可能感兴趣的:(LeeCode457-环形数组循环)