[코딩테스트 Python3(6)] Graph 그래프
그래프 G(V, E)는 어떤 자료나 개념을 표현하는 정점(vertex)들의 집합 V와 이들을 연결하는 간선(edge)들의 집합 E로 구성된 자료구조
그래프 종류
- 방향 그래프 vs. 무향 그래프
- 다중 그래프 vs. 단순 그래프
- 가중치 그래프 → 다익스트라
인접 리스트(adjacency list)
- 대칭으로 구성되고, 가운데 대각선은 0으로 표현된다.
- 위와 같은 방식은 0을 명시해야 하다보니 메모리 낭비가 심하다.
- 따라서 아래와 같이 표현한다.
graph = {
1: [3,5],
2: [4,5],
3: [1,5],
4: [2,5],
5: [1,2,3,4]
}
암시적 그래프(Implicit graph)
암시적 그래프라는 용어의 의미는 암시적으로 연결이 모두 되어있다고 간주(연결에 대한 부분은 명시하기 않음)하기 때문이다.
그래프 순회 Traversal
그래프 순회란, 그래프 탐색(search)라고도 불리우며 그래프의 각 정점을 방문하는 과정
그래프 순회 방법에는 크게 1) 너비 우선 탐색(BFS) 2) 깊이 우선 탐색(DFS)의 2가지 알고리즘이 있다.
너비 우선 탐색(BFS; Breadth-First search) 그래프 순회
BFS는 시작 노드에서 가까운 vertex 먼저 순회하는 특징이 있다.
from collections import deque
def bfs(graph, start_v):
visited = [start_v]
queue = deque(start_v)
while queue:
cur_v = queue.popleft()
for v in graph[cur_v]:
if v not in visited:
visited.append(v)
queue.append(v)
return visited
bfs(graph, 'A')
깊이 우선 탐색(DFS; Depth-first search) 그래프 순회
graph = {...}
visited = []
def dfs(cur_v):
visited.append(cur_v)
for v in graph[cur_v]:
if v not in visited:
dfs(v)
dfs('A')
재귀함수로 코드가 실행된다.
https://leetcode.com/problems/number-of-islands/
Number of Islands - LeetCode
Can you solve this real interview question? Number of Islands - Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent l
leetcode.com
이런 문제는 암시적 그래프로 생각하고 풀어야 한다.
from collections import deque
def numIslands(grid):
number_of_islands = 0
row = len(grid)
col = len(grid[0])
visited = [[False]*col for _ in range(row)]
def bfs(x,y):
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1] #상하좌우를 표현
#만약 대각선의 경우도 고려한다면, dx = [-1, 1, 0, 0, 1. -1, 1, -1] & dy = [0, 0, -1, 1, 1, -1, -1, 1]
visited[x][y] = True
#deque는 바로 값을 넣은채로 선언하면 오류가 발생할 수 있다.
queue = deque()
queue.append((x,y))
while queue:
cur_x, cur_y = queue.popeleft()
for i in range(4):
next_x = cur_x + dx[i]
next_y = cur_y + dy[i]
if next_x >= 0 and next_x < m and next_y >= 0 and next_y < n:
if grid[next_x][next_y] == "1" and not visited[next_x][next_y]:
visited[next_x][next_y] = True
queue.append((next_x, next_y))
for i in range(m):
for j in range(n):
if grid[i][j] == "1" and not visited[next_x][next_y]:
bfs(i, j)
number_of_islands += 1
return number_of_islands
https://leetcode.com/problems/shortest-path-in-binary-matrix/description/
Shortest Path in Binary Matrix - LeetCode
Can you solve this real interview question? Shortest Path in Binary Matrix - Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear path, return -1. A clear path in a binary matrix is a path from
leetcode.com
최단거리 문제 → BFS로 푸는 것이 맞다! 왜냐하면 BFS는 가까운 곳부터 방문하기 때문이다!
가까운 곳부터 순차적으로 방문하기 때문에 출발지에서 가장 가까운 곳부터 빠르게 찾을 수 있다.
BFS는 Queue를 쓴다는 것을 떠올려야 한다.
from collections import deque
def shortestPathBinaryMatrix(grid):
shortest_path_len = -1
row = len(grid)
col = len(grid[0])
if grid[0][0] != 0 or grid[row-1][col-1] != 0: return -1
#무한루프에 빠지지 않으려면 이미 방문했다는 것을 표현해야 함
visited = [[False] * col for _ in range(row)]
#8가지 방향을 모두 확인한다.
delta = [(1,0), (-1,0), (0,1), (0,-1),
(1,1), (1,-1), (-1,1), (-1,-1)]
#deque는 선언할 때 초기화하면 안되고, 선언하고 그 다음에 값을 초기화한다.
queue = deque()
queue.append((0,0,1))
visited[0][0] = True
while queue:
cur_r, cur_c, cur_len = queue.popleft()
#목적지에 도착했을 때 그 때의 cur_len를 shortest_path_len에 저장하면 된다.
if cur_r == row - 1 and cur_c == col - 1:
shortest_path_len = cur_len
break
#연결되어 있는 vertex 확인하기
for dr, dc in delta:
next_r = cur_r + dr
next_c = cur_c + dc
if row > next_r and next_r >= 0 and col > next_c and next_c >= 0:
if grid[next_r][next_c] == 0 and not visited[next_r][next_c]:
queue.append((next_r, next_c, cur_len + 1))
visited[next_r][next_c] = True
return shortest_path_len
https://leetcode.com/problems/keys-and-rooms/
Keys and Rooms - LeetCode
Can you solve this real interview question? Keys and Rooms - There are n rooms labeled from 0 to n - 1 and all the rooms are locked except for room 0. Your goal is to visit all the rooms. However, you cannot enter a locked room without having its key. Whe
leetcode.com
머리 깨지는군!
DFS로 문제로 해결한다.
def canVisitAllRooms(rooms):
visited = [False] * len(rooms) #5번방 방문 안했네? => False // True
# v에 연결되어 있는 모든 vertex 방문할거다.
def dfs(v):
visited[v] = True
for next_v in rooms[v]:
#in 연산자는 강력하다 but 리스트에서 in을 쓰면 시간복잡도가 너무 커진다.
if visited[next_v] == False:
dfs(next_v)
dfs(0)
if len(visited) == len(rooms):
return True
else:
return False
return visited