그래프 탐색 알고리즘 : DFS / BFS
탐색(Search)란 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정을 말한다.
대표적인 그래프 탐색 알고리즘으로는 DFS 와 BFS가 있다.
스택과 큐
스택 (STACK)
- 먼저 들어 온 데이터가 나중에 나가는 형식(선입후출)의 자료구조이다. → FILO 구조
- 파이썬에서 스택자료구조를 이용하기 위해서는 단순히 리스트를 이용하면 된다.
ex) 박스쌓기
- 원소 삽입 : .append()
- 원소 삭제 : .pop()
큐 (Queu)
- 먼저 들어온 데이터가 먼저 나가는 형식(선입선출)의 자료구조이다.→ FIFO 구조
- 맛집 줄서기
리스트 자료형을 이용하여 큐를 구현할 수 있지만, 시간복잡도가 높아 deque 라이브러를 사용하는 것이 좋다.
- 원소삽입 : .append()
- 원소삭제 : .popleft()
재귀함수(Recursive Function)
자기 자신을 다시 호출하는 함수이다.
ex) 단순한 형태의 재귀 함수 예제
def recursive_function() :
print("재귀 함수를 호출합니다.")
recursive_function()
recursive_function()
"재귀 함수를 호출합니다" 라는 문자열을 무한히 출력한다.
어느 정도 출력하다가 최대 재귀 깊이 초과 메시지가 출력된다.
종료조건
종료조건을 제대로 명시하지 않으면 함수가 무한히 호출 될 수 있다.
def recursive_function(i) :
if i == 10: #10번째 호출을 했을 대 종료되도록 종료 조건 명시
return
print(i , "번째 재귀 함수에서" , i+1 , "번째 재귀함수를 호출합니다.")
recursive_function(i+1)
print(i, "번째 재귀함수를 종료합니다.")
recursive_function(1)
팩토리얼 구현 예제
#반복적으로 구현한 n!
def factorial_recursive(n) :
result = 1
#n이 1이 될 때까지 반복
while n == 1 :
result *= n #1부터 n까지 차례대로 곱하기
n -= 1
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)
수학적으로 정의된 점화식을 그대로 이용한다는 점에서 코드가 더 간결하고 직관적일 수 있다는 점이 좋게 볼 수 있다.
최대공약수(유클리드 호제법) 예제
두 개의 자연수에 대한 최대공약수를 구하는 대표적인 알고리즘
유클리드 호제법
- 두 자연수 A,B에 대하여 A를 B로 나눈 나머지를 R이라고 합시다.
- 이때 A와 B의 최대공약수는 B와 R의 최대공약수와 같습니다.
def gcd(a,b) :
if a % b == 0 :
return b
else :
return gcd(b , a%b)
- 재귀함수는 잘 활용하면 복잡한 알고리즘을 간결하게 작성할 수 있지만, 오히려 다른 사람이 이해하기 어려운 형태의 코드가 될 수 있으므로 신중하게 사용해야 한다.
- 재귀함수는 반복문을 이용하여 동일한 기능을 구현할 수 있다. (반대도 가능)
- 컴퓨터가 함수를 연속적으로 호출하면 컴퓨터 메모리 내부의 스택 프레임이 쌓이므로, 스택을 사용해야할 때 구현상 스택 라이브러리 대신에 재귀함수를 이용하는 경우가 많다.
DFS(Depth-First Search)
DFS는 깊이 우선 탐색이라고도 부르며 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘입니다.
DFS는 스택 자료구조(혹은 재귀 함수)를 이용하며, 구체적인 동작 과정은 다음과 같습니다.
- 탐색 시작 노드를 스택에 삽입하고 방문 처리를 합니다.
- 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문에 처리합니다. 가장 → 작은 노드를 우선적으로 방문한다.
- 더 이상 2번의 과정을 수행할 수 없을 때 까지 반복한.
최대한 깊게 들아가는 형태로 동작을 하기 때문에 스택 대신에 재귀함수를 이용하여 구현할 수 있다.
#각 노드가 연결된 정보를 표현 (2차원 리스트)
graph = [
[] ,
[2,3,8], #보통 1번 노드부터 시작
[1,7], #2번 노드가 연결된 노드
[1,4,5],
[3,5],
[3,4],
[7],
[2,6,8],
[1,7]
]
#각 노드가 방문된 정보를 표현 (1차원 리스트)
visited = [False] * 9
#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(graph , 1 , visited)
#output
1 2 7 6 8 3 4 5
BFS(Breadth-First Search)
BFS는 너비 우선 탐색이라고도 부르며, 그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘이다.
BFS는 큐 자료구조를 이용하며, 구체적인 동작 과정은 다음과 같다.
- 탐색 시작 노드를 큐에 삽입하고 방문 처리를 한다.
- 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리
- 더 이상 2번의 과정을 수행할 수 없을 때 까지 반복한다.
#각 노드가 연결된 정보를 표현 (2차원 리스트)
graph = [
[] ,
[2,3,8], #보통 1번 노드부터 시작
[1,7], #2번 노드와 인접하게 연결된 노드
[1,4,5],
[3,5],
[3,4],
[7],
[2,6,8],
[1,7]
]
#각 노드가 방문된 정보를 표현 (1차원 리스트)
visited = [False] * 9
from collections import deque
#BFS 정의
def bfs(graph , start , visited) :
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
'STUDY > Algorithm' 카테고리의 다른 글
[Algorithm] 이분탐색 (0) | 2024.05.14 |
---|---|
[Algorithm] 백준 - ReMorse (0) | 2024.05.06 |
[Algorithm] 그리디 알고리즘 (0) | 2024.04.30 |
[Algorithm] 알고리즘(코딩테스트) 스터디 계획 (0) | 2024.04.25 |
[Algorithm] 프로그래머스 - OX퀴즈 (0) | 2024.04.23 |