841_Keys and Rooms

2 minute read

https://leetcode.com/problems/keys-and-rooms/

DFS를 사용하여 해결

DFS는 깊이 우선 탐색이라는 뜻이다.
한쪽 노드를 타고 가장 깊은 곳 까지 같다가 더이상 갈 노드가 없으면 돌아오는 방식이다.
이 방식의 단점은 특정 문제의 경우 노드가 무한히 깊어져서 빠져나오지 못하게 될 수도 있다.
또한 최단 거리를 구하는 문제의 경우 처음으로 목적지를 만나도 최단거리인지 알 수가 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution(object):
    def canVisitAllRooms(self, rooms):
        """
        :type rooms: List[List[int]]
        :rtype: bool
        """
        def dfs(rooms, start):
            
            print(rooms, start)
            tovisit     = [ start ]
            visited     = [ False for _ in range(len(rooms)) ]
            visited[0]  = True

            
            while tovisit:
                key_list = tovisit.pop()    
                #print(key_list,"KEY_LIST")
                
                for key in key_list:
                    
                    if visited[key] == False:
                        visited[key] = True
                        tovisit.append(rooms[key])
                        
            for v in visited:
                if v == False:
                    return False
                
            return True               
                    
                
        return dfs(rooms, rooms[0])

스택이라는 FILO 자료형을 이용하면 DFS를 재귀없이 구현할 수 있다.

방문할 스택이 필요하다
방문한 곳은 rooms의 갯수와 동일하게 False 방문하지 않았다고 설정해준다.
방문할 곳의 key_list들을 tovisit스택에 추가하고

tovisit 스택을 계속 채워나간다.

이미 방문한 곳의 열쇠는 추가하지 않기 위해
if visited[key] == False 조건이 필요하다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution(object):
    def canVisitAllRooms(self, rooms):
        """
        :type rooms: List[List[int]]
        :rtype: bool
        """
        
        
        def dfs_visit(rooms, start_key, visited):
            #print(start_key,"START_KEY", visited)
            for key in start_key:
                if visited[key] == False:
                    visited[key] = True
                    dfs_visit(rooms, rooms[key], visited)
            
        def dfs(rooms):
                        
            visited     = [False for _ in range(len(rooms))]
            visited[0]  = True
            dfs_visit(rooms, rooms[0], visited)
        
            #print(visited,"VISITED")
            for v in visited:
                if v == False:
                    return False
                
            return True
            
        return dfs(rooms)

는 recursive로 코드를 짠 것이다.

dfs_visit이라는 함수를 따로 만들어주고,
처음 시작점을 지정해주고(rooms[0])
visited라는 방문했는지 여부를 체크하는 list를 넘겨준다.

처음에 방에 들은 키(start_key)를 탐색하면서
방문하지 않았다면 방문하고 그 방에 들은 key list(rooms[key]를 다시 재귀적으로 자신의 함수로 호출한다.

인풋값이 아래와 같을때, [[1,3],[3,0,1],[2],[0]]

처음 dfs_visit(rooms, rooms[0], visited)는 다음과 같은 인자를 갖는다.

rooms = [[1,3], [3,0,1], [2], [0]]
rooms[0] = [1,3]
visited = [True, False, False, False]

visited[0]이 True인 것은 첫번째 방문은 처음에 열면서 [1,3]를 발견하기 때문이다.

11번 줄에서

start_key는 [1,3]이고
1번 방문의 키로 visited[key]를 확인한다 그리고 방문하지 않았다면 방문했다고 체크해주고
그 방에 놓여진 키(rooms[key])를 다시 재귀적으로 호출하여 방문한다.

1번 방문을 열고 key list를 얻는다
visited = [True, True, False, False]
rooms[1]은 [3,0,1]이다

다시 3번 방문을 열고 key_list를 얻는다
visited = [ True, True, False, True] rooms[3] = [0]이다.

0번은 이미 방문했다 (visited[0] == False)
백트래킹해서 다시 돌아오면

[3,0,1]에서 3번 방문을 열었으니,
0번 방문을 다시 열어본다.

0번은 이미 방문했다 (visited[0] == False)
백트래킹해서 다시 돌아오면

[3,0,1]에서 0번 방문을 열었으니,
1번 방문을 다시 열어본다.

1번은 이미 방문했다 (visited[1] == False)
백트래킹해서 다시 돌아간다.

그럼 맨처음에 [1,3]에서 1번 방문을 재귀적으로 호출한 것이 끝난것이다.
3번 방문을 마찬가지로 하면

최종 visited = [ True, True, False, True ] 가 된다

(방문했는지 확인하는 여부는 무한루프에 빠지지 않기 위함이다.)
이미 방문했다면 백트래킹하게 된다.