-
[알고리즘 + Python] DFS(Depth-First Search, 깊이우선탐색) & BFS(Breadth-First Search, 너비우선탐색)CS/알고리즘 2023. 2. 2. 21:27반응형
해당 게시물은 동빈나님 유튜브 "(이코테 2021 강의 몰아보기) 3. DFS & BFS"를 보고 학습용으로 기록한 것입니다.
Stack과 Queue 자료구조
1) Stack
파이썬에서 스택은 리스트 자료구조를 사용하면 된다.
cf) stack[::-1] : 최상단 원소부터 출력
stack = [] stack.append(5) # 삽입 stack.append(2) stack.append(3) stack.append(7) stack.pop() # 제거 stack.append(1) stack.append(4) stack.pop() # 최상단 원소부터 출력 print(stack[::-1]) # [1, 3, 2, 5] # 최하단 원소부터 출력 print(stack) # [5, 2, 3, 1]
2) Queue
리스트 자료형을 이용해서 queue를 구현할 수도 있지만 시간복잡도가 더 높아서 비효율적으로 동작할 수도 있다.
따라서 deque 라이브러리를 이용하도록 하자.
from collections import deque # 큐(Queue) 구현을 위해 deque 라이브러리 사용 queue = deque() queue.append(5) # 삽입 queue.append(2) queue.append(3) queue.append(7) queue.popleft() # 제거 queue.append(1) queue.append(4) queue.popleft() # 먼저 들어온 순서대로 출력 print(queue) # deque([3, 7, 1, 4]) # 역순으로 바꾸기 queue.reverse() # 나중에 들어온 원소부터 출력 print(queue)
재귀함수(Recursive Function)
재귀함수를 문제 풀이에서 사용할 때는 재귀함수의 종료 조건을 반드시 명시해야 한다.
def recursive_function(i): # 100번째 호출을 했을 때 종료되도록 종료 조건 명시 if i == 100: return print(i, '번째 재귀함수에서', i + 1, '번째 재귀함수를 호출합니다.') recursive_function(i + 1) print(i, '번째 재귀함수를 종료합니다.') recursive_function()
1) 재귀함수 사용의 유의사항
- 모든 재귀함수는 반복문을 이용하여 동일한 기능을 구현할 수 있다.
- 재귀함수가 반복문보다 유리한 경우도 있고 불리한 경우도 있다.
- 컴퓨터가 함수를 연속적으로 호출하면 컴퓨터 메모리 내부의 스택 프레임에 쌓인다.
- 그래서 스택을 사용해야 할 때 구현상 스택 라이브러리 대신 재귀함수를 이용하는 경우가 많다.
2) 팩토리얼 예시
: 반복적으로 구현 vs 재귀적으로 구현
cf) 수학적으로 0!과 1!은 1이다.
# 반복적으로 구현한 n! def factorial_iterative(n): result = 1 # 1부터 n까지의 수를 차례대로 곱하기 for i in range(1, n + 1): result *= i return result # 재귀적으로 구현한 n! def factorial_recursive(n): if n <= 1: # n이 1 이하인 경우 1을 반환 return 1 # n! = n * (n - 1)!를 그대로 코드로 작성하기 return n * factorial_recursive(n - 1) # 각각의 방식으로 구현한 n! 출력(n = 5) print('반복적으로 구현:', factorial_iterative(5)) # 반복적으로 구현: 120 print('재귀적으로 구현:', factorial_recursive(5)) # 재귀적으로 구현: 120
DFS(Depth-First Search)
DFS는 깊이우선탐색이라고도 부르며 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘이다.
DFS는 스택 자료구조(혹은 재귀함수)를 이용하며, 구체적인 동작과정은 다음과 같다.
- 탐색 시작 노드를 스택에 삽입하고 방문처리를 한다.
- 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문처리한다. 방문하지 않은 인접노드가 없으면 스택에서 최상단 노드를 꺼낸다.
- 더 이상 2번의 과정을 수행할 수 없을 때까지 반복한다.
1) DFS 동작 예시
DFS는 인접한 노드가 여러 개 일수도 있기 때문에 어떤 노드부터 방문할 지에 대한 기준은 문제에서 요구하는 것에 따른다.
2) DFS 소스코드 예제(Python)
# 각 노드가 연결된 정보를 표현 (2차원 리스트) graph = [ [], # idx 0 은 사용하지 않기 위함 [2, 3, 8], [1, 7], [1, 4, 5], [3, 5], [3, 4], [7], [2, 6, 8], [1, 7] ] # 각 노드가 방문된 정보를 표현 (1차원 리스트) visited = [False] * 9 # idx 0 은 사용하지 않기 위해 9(8+1)로 초기화함 # 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) # 정의된 DFS 함수 호출 dfs(graph, 1, visited) # 실행결과: 1 2 7 6 8 3 4 5
BFS(Breadth-First Search)
BFS는 너비우선탐색이라고도 부르며 그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘이다.
BFS는 큐 자료구조를 이용하며, 구체적인 동작과정은 다음과 같다.
- 탐색 시작 노드를 큐에 삽입하고 방분처리를 한다.
- 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리한다.
- 더 이상 2번의 과정을 수행할 수 없을 때까지 반복한다.
1) BFS 동작 예시
2) BFS 소스코드 예제(Python)
from collections import deque # 각 노드가 연결된 정보를 표현 (2차원 리스트) graph = [ [], # idx 0 은 사용하지 않기 위함 [2, 3, 8], [1, 7], [1, 4, 5], [3, 5], [3, 4], [7], [2, 6, 8], [1, 7] ] # 각 노드가 방문된 정보를 표현 (1차원 리스트) visited = [False] * 9 # idx 0 은 사용하지 않기 위해 9(8+1)로 초기화함 # 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 # 정의된 BFS 함수 호출 bfs(graph, 1, visited) # 1 2 3 8 7 4 5 6
연습문제
1. 음료수 얼려 먹기
import sys input = sys.stdin.readline n, m = map(int, input().split()) # 2차원 리스트의 맵 정보 입력받기 graph = [] for i in range(n): graph.append(list(map(int, input()))) # DFS로 특정 노드를 방문하고 연결된 모든 노드들도 방문 def dfs(x, y): # 주어진 범위를 벗어나는 경우에는 즉시 종료 if x <= -1 or x >= n or y <= -1 or y >= m: return False # 현재 노드를 아직 방문하지 않았다면 if graph[x][y] == 0: graph[x][y] = 1 # 상, 하, 좌, 우의 위치들도 모두 재귀적으로 호출 dfs(x, y + 1) dfs(x, y - 1) dfs(x - 1, y) dfs(x + 1, y) 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)
출처: https://www.youtube.com/watch?v=7C9RgOcvkvo&list=PLRx0vPvlEmdAghTr5mXQxGpHjWqSz0dgC&index=4
728x90'CS > 알고리즘' 카테고리의 다른 글
[알고리즘 + Python] 최단 경로 알고리즘 - 다익스트라(dijkstra), 플로이드 워셜(Floyd-Warshall) (0) 2023.02.05 [알고리즘 + Python] DP(Dymanic Programming, 다이나믹 프로그래밍) (0) 2023.02.02