그래프 탐색 알고리즘

HeeSeong·2021년 3월 6일
0

파이썬 코딩테스트

목록 보기
3/12
post-thumbnail
post-custom-banner

Ⅰ. 그래프 탐색 알고리즘


  • 탐색이란 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정

  • 대표적으로 DFS, BFS

  • 코딩 테스트에서 매우 자주 등장하는 유형


① DFS (깊이 우선 탐색)


  • 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘

  • 스택 자료구조 혹은 재귀 함수를 이용

  • 모든 노드를 방문하고자 하는 경우

  • 트리가 깊은 것보다 엄청나게 넓을 때

  • 트리의 깊이가 깊은데 해답들이 많을 경우


  1. 탐색 시작 노드를 스택에 삽입하고 방문 처리

  2. 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 기준에 따라 그들 중 하나를 스택에 넣고 방문 처리,
    방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다.

  3. 2번 과정을 더이상 수행할 수 없을 때까지 반복

파이썬 구현

# DFS 함수 정의
def dfs(graph, v, visited):
    # 현재 노드를 방문 처리
    visited[v] = True
    # 방문한 곳 출력
    print(v, end=' ')
    # 현재 노드와 연결된 다른 노드를 재귀적으로 방문
    for i in graph[v]:
    	# 방문을 안했다면
        if not visited[i]:
            # 재귀함수로 방문 진행
            dfs(graph, i, visited)

# 각 노드가 연결된 정보를 리스트 자료형으로 표현(2차원 리스트) 
# 0번째는 비워두고 안쓴다. 첫번째 노드에 연결된 노드 번호 = [2, 3, 8]
graph = [
  [],
  [2, 3, 8],
  [1, 7],
  [1, 4, 5],
  [3, 5],
  [3, 4],
  [7],
  [2, 6, 8],
  [1, 7]
]

# 각 노드가 방문된 정보를 리스트 자료형으로 표현(1차원 리스트)
# 0번째는 안쓰므로 하나 더 크게 만들어줌 (8+1)
visited = [False] * 9

# 정의된 DFS 함수 호출
dfs(graph, 1, visited)

② BFS (너비 우선 탐색)


  • 그래프에서 가까운 노드를 우선적으로 탐색하는 알고리즘

  • 큐 자료구조를 이용

  • 최단경로 문제에서 주로 이용

  • 그렇게 깊지 않고 해답이 트리의 뿌리에서 그닥 떨어져 있지 않을 때

  • 트리의 깊이가 상당히 깊은데 해답이 몇 개 없는 경우


  1. 탐색 시작 노드를 큐에 삽입하고 방문 처리

  2. 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리

  3. 2번 과정을 더이상 수행할 수 없을 때까지 반복

파이썬 구현

from collections import deque

# BFS 함수 정의
def bfs(graph, start, visited):
    # 큐(Queue) 구현을 위해 deque 라이브러리 사용
    queue = deque([start])
    # 현재 노드를 방문 처리
    visited[start] = True
    # 큐가 빌 때까지 반복
    while queue:
        # 큐에서 하나의 원소를 뽑아 출력
        v = queue.popleft()
        print(v, end=' ')
        # 해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입
        for i in graph[v]:
            if not visited[i]:
                queue.append(i)
                visited[i] = True

# 각 노드가 연결된 정보를 리스트 자료형으로 표현(2차원 리스트)
graph = [
  [],
  [2, 3, 8],
  [1, 7],
  [1, 4, 5],
  [3, 5],
  [3, 4],
  [7],
  [2, 6, 8],
  [1, 7]
]

# 각 노드가 방문된 정보를 리스트 자료형으로 표현(1차원 리스트)
visited = [False] * 9

# 정의된 BFS 함수 호출
bfs(graph, 1, visited)

Ⅱ. 예제


① 음료수 얼려 먹기 (DFS)


❔ 문제 설명


N * M (세로, 가로) 크기의 얼음 틀이 있습니다. 구멍이 뚫려 있는 부분은 0, 칸막이가 존재하는 부분은 1로 표시됩니다.

구멍이 뚫려 있는 부분끼리 상 하 좌 우로 붙어 있는 경우 서로 연결되어 있는 것으로 간주합니다.

이때 얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수를 구하는 프로그램을 작성하세요.


💡 문제 해결 아이디어


  • 특정한 지점의 주변 상 하 좌 우를 살펴본 뒤에 주변 지점 중에서 값이 0이면서 아직 방문하지 않느 지점이 있다면 해당 지점을 방문한다.

  • 방문한 지점에서 다시 상 하 좌 우를 살펴보면서 방문을 진행하는 과정을 반복하면 연결된 모든 지점을 방문할 수 있다.

  • 모든 노드에 대하여 1, 2 번 과정을 반복하며 방문하지 않은 지점의 수를 카운트한다.


✔️ 풀이

Python

# N, M을 공백을 기준으로 구분하여 입력 받기
n, m = map(int, input().split())

# 2차원 리스트의 맵 정보 입력 받기
# 00110 한 줄 입력시 graph = [ [0, 0, 1, 1, 0] .... ]
graph = []
for i in range(n):
    graph.append(list(map(int, input())))

# DFS로 특정한 노드를 방문한 뒤에 연결된 모든 노드들도 방문
def dfs(x, y):
    # 주어진 범위를 벗어나는 경우에는 즉시 종료
    # 인덱스 범위는 0 ~ n-1 이므로
    if x <= -1 or x >= n or y <= -1 or y >= m:
        return False
    # 현재 노드를 아직 방문하지 않았다면
    if graph[x][y] == 0:
        # 해당 노드 방문 처리
        graph[x][y] = 1
        # 상, 하, 좌, 우의 위치들도 모두 재귀적으로 호출
        # 아래 4개는 단순 방문처리 (0 -> 1) 하는 용도만 기능 
        dfs(x - 1, y) # 상
        dfs(x, y - 1) # 좌
        dfs(x + 1, y) # 하
        dfs(x, y + 1) # 우
        return True
    return False

# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):
    for j in range(m):
        # 현재 위치에서 DFS 수행
        if dfs(i, j) == True:
            result += 1

print(result) # 정답 출력

② 미로 탈출 (BFS)


❔ 문제 설명


N * M 크기의 직사각형 형태의 미로에 갇혔습니다. 미로에는 괴물이 있어서 피해서 탈출해야 한다.

현재 위치는 (1, 1)이며 미로의 출구는 (N, M)의 위치에 존재하며 한 번에 한 칸씩 이동할 수 있습니다. 이때 괴물이 있는 부분은 0으로, 괴물이 없는 부분은 1로 표시되어 있습니다.

탈출하기 위해 움직여야 하는 최소 칸의 개수를 구하세요. (칸을 셀 때는 시작 칸과 마지막 칸을 포함해서 계산)


💡 문제 해결 아이디어


  • BFS는 시작 지점에서 가까운 노드부터 차례대로 그래프의 모든 노드를 탐색합니다

  • 상 하 좌 우로 연결된 모든 노드로의 거리가 1로 동일합니다.


✔️ 풀이

Python

from collections import deque

# N, M을 공백을 기준으로 구분하여 입력 받기
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for i in range(n):
    graph.append(list(map(int, input())))

# 이동할 네 가지 방향 정의 (상, 하, 좌, 우)
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

# BFS 소스코드 구현
def bfs(x, y):
    # 큐(Queue) 구현을 위해 deque 라이브러리 사용
    queue = deque()
    queue.append((x, y))
    # 큐가 빌 때까지 반복하기
    while queue:
        x, y = queue.popleft()
        # 현재 위치에서 4가지 방향으로의 위치 확인
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            # 미로 찾기 공간을 벗어난 경우 무시
            if nx < 0 or nx >= n or ny < 0 or ny >= m:
                continue
            # 벽인 경우 무시
            if graph[nx][ny] == 0:
                continue
            # 해당 노드를 처음 방문하는 경우에만 최단 거리 기록
            if graph[nx][ny] == 1:
                graph[nx][ny] = graph[x][y] + 1
                queue.append((nx, ny))
    # 가장 오른쪽 아래까지의 최단 거리 반환
    # 해당 위치 노드에 시작점부터 거리 수로 숫자를 바꿔줬으므로 
    return graph[n - 1][m - 1]

# BFS를 수행한 결과 출력
print(bfs(0, 0))
profile
끊임없이 성장하고 싶은 개발자
post-custom-banner

0개의 댓글