일단 문제를 보면 LinkedList 와 유사한 방법으로 풀 수 있다는 것을 알 수가 있다.

왜냐하면 현재의 Node 에서 갈수 있는 next Node 가 1개 밖에 없기 때문이다.

가장 간단한 방법을 생각해보면 각 Node 에서마다 Circular Array Loop 가 되는지 확인해 보는 것이다.

class Solution:
    def circularArrayLoop(self, nums: List[int]) -> bool:
        if not nums or len(nums) < 2:
            return False
        
        n = len(nums)
        for i in range(n):           
            path = dict()
            k = i
            order = 0
            while k not in path and nums[i] * nums[k] > 0:
                path[k] = order
                order+=1
                k = (k + nums[k]) % n
						# Loop 가 만들어 졌는지 k in path 로 확인
						# order - path[k] == 1 이라는 것은 현재 loop 가 길이가 1이상인걸 확인
            if k in path and order - path[k] > 1:
                return True
                
        return False
class Solution:
    def circularArrayLoop(self, nums: List[int]) -> bool:
        if not nums or len(nums) < 2:
            return False
        
        n = len(nums)
        for i in range(n):           
            path = set()
            k = i
            while k not in path and nums[i] * nums[k] > 0:
                path.add(k)
                k = (k + nums[k]) % n
						# 길이가 1 이상인건 nums[k]%n != 0 로 확인 가능.
            if k in path and nums[k]%n != 0:
                return True
                
        return False

위의 방법들은 시간복잡도 O(N^2) 공간복잡도 O(N) 이 된다.

일반적으로 Graph 에서 Cycle 을 찾을때 grey-black 으로 색깔을 칠하는 방법이 유명하다

위의 유튜브을 보면 쉽게 이해가 될 것이다.

핵심은 현재 visiting 중인 것을 grey, visited 인 것을 black 에 넣는다.

즉 visiting 과 visited, not visit 을 구분해서 그래프을 순회한다. 만약 visiting 인 node 을 만나면 그 때 cycle 이 발생한 것이다.

밑은 이러한 방법을 통해서 구현한 코드이다.

class Solution:
    def circularArrayLoop(self, nums: List[int]) -> bool:
        grey, black = set(), set()
        
        def find_circle(node, dirt):
            # change direction
            if nums[node] * dirt < 0:
                return False
            if node in grey:
                return True
            if node in black:
                return False
            grey.add(node)
            nxt = (node + nums[node]) % len(nums)
						# 1개로만 cycle 을 만들면 안된다.
            if nxt != node and find_circle(nxt, nums[node]) == True:
                return True
            grey.remove(node)
            black.add(node)
            return False
            
        for i in range(len(nums)):
            if find_circle(i, 1):
                return True
        grey, black = set(), set()
        for i in range(len(nums)):
            if find_circle(i, -1):
                return True
        return False

숫자의 범위가 나와있기 때문에 1000 보다 큰 수을 이용해서 공간복잡도을 줄일 수 있다.

밑은 grey, black 을 set 을 사용하는 대신에 배열에 숫자을 더해서 표현하였다.