DFS(depth-first search) - 깊이우선탐색
그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘.
LIFO 스택 자료구조(혹은 재귀함수)를 이용
DFS 동작과정
- 탐색 시작 노드를 스택에 삽입하고 방문 처리를 한다.
- 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문 처리.
방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다. - 더이상 2번 과정을 수행할 수 없을 때까지 반복.
Backtracking
- DFS에서 활용되는 방법, 노드가 있을만한 곳을 되돌아가서 모두 살펴본다는 개념
- 위의 그림처럼 DFS는 깊이우선적으로 탐색을 진행하고, 재귀적으로 아래에서부터 탐색하지 않은 정점이 있는지 확인하는 방법으로 되돌아가는 것에서 backtracking사용.
- 이로 말미암아 시간이 오래걸린다는 단점이 발생함. 최단거리를 찾는데 문제가 있음.
DFS 소스코드 예제
'''DFS 소스코드 예제'''
# DFS 메소드 함수
def dfs(graph, v, visited): # v는 탐색을 시작하는 노드드
#현재노드를 방문 처리한다
visited[v] = True
print (v, end=' ')
#현재노드와 연결된 다른 노드를 재귀적으로 방문
for i in graph[v]:
if not visited[i]: # 인접한 노드가 방문되지 않은 상태라면
dfs (graph, i, visited) # 재귀함수로 방문
# 인접노드 딕셔너리 형태로 표현
graph = {
1: [2,3,4],
2: [5],
3: [6],
4: [],
5: [7],
6: [5,7],
7: [6]}
# 각 노드가 방문된 정보를 표현(1차원 리스트)하는 리스트 초기화
visited= [False]*8 # 노드의 개수 n+1 를 곱해주어야 한다.
# DFS 함수 호출
dfs(graph, 1, visited)
'''1 2 5 7 6 3 4'''
dfs 함수 구현 방법
- 재귀적 함수 구현
- 코드가 간결함
- 스택 함수 구현
- 위에서 아래로 코드를 해석하면 되어서 직관적이고 이해가 쉬움
- 후입선출 개녕를 적용하여 마지막 삽임된 노드를 기준으로 깊이우선탐색을 진행
- 재귀와 다르게 역순으로 진행
- 실행속도가 재귀보다 빠름
dfs 활용의 예
- 가중 그래프의 최소 스패닝 트리 찾기
- 길 찾기
- 그래프에서 주기 감지
- 미로 문제
BFS(breadth-first search) - 너비우선탐색
그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘,
FIFO 큐 자료구조를 이용한다.
BFS 동작과정
- 탐색 시작 노드를 큐에 삽입하고 방문 처리
- 큐에서 노드를 꺼낸뒤에 해당 노드의 인접 노드 중 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리
- 더이상 2번의 과정을 수행 할 수 없을 때까지 반복
시작 노드에서 제일 멀리 떨어진 노드를 가장 마지막에 구하는 방식이며. 이런 특징때문에 각 간선의 비용이 모두 동일한 상황에서 최단거리를 문제를 해결하기 위한 방식으로 사용되기도 한다.
BFS 소스코드 예제
'''BFS 소스코드 예제'''
from collections import deque
# bfs메소드 함수 정의
def bfs(graph, start, visited):
# 큐 구현을 위해 deque라이브러리 사용
queue = deque([start])
# 현재노드를 방문처리
visited[start] = True
# 큐가 빌때까지 반복
while queue:
# 큐에서 하나의 원소를 뽑아 출력하기 queue응 popleft사용
v = queue.popleft()
print(v, end=' ')
# 아직 방문하지 않은 인접한 노드들을 큐에 삽입
for i in graph[v]:
if not visited[i]:
queue.append(i)
visited[i] = True # 들어온 노드들 모두 방문처리
graph = {
1: [2,3,4],
2: [5],
3: [6],
4: [],
5: [7],
6: [5,7],
7: [6]}
# 각 노드가 방문한 정보를 표현, 초기화 (1차원 리스트)
visited = [False]*8
# bfs 함수 호출
bfs(graph, 1, visited)
bfs 활용의 예
- 길 찾기, 라우팅
- BitTorrent와 같은 P2P 네트워크에서 인접 노드 찾기
- 웹 크롤러
- 소셜 네트워크에서 멀리 떨어진 사람 찾기
- 그래프에서 주변 위치 찾기
- 네트워크에서 방송
- 그래프에서 주기 감지
- 연결된 구성 요소 찾기
- 몇 가지 이론적 그래프 문제 풀기
BFS와 DFS 비교
- DFS 장점
- 찾는 노드의 depth가 깊을수록 빠르다.
- 메모리를 적게 차지 한다.
- BFS 장점
- 최단경로를 찾기 적합하다.
- 찾는 노드가 인접한 경우 효과적이다.
- DFS 단점
- 노드가 무한한 갯수로 존재하는 경우, 무한반복(무한루프)에 빠진다.
- BFS 단점
- Queue를 이용해 노드를 저장하기 때문에 노드의 수가 많을수록 메모리를 많이 소비한다.
예시 문제 1.
'''음료수 얼려먹기 문제'''
'''
첫번째 줄에 얼음 들의 세로길이 n과 m이 주어진다. (n >=1 ,m <= 1000)
주번째 줄부터 n+1번째 줄까지 얼음틀의 형태가 주어진다.
이때 구멍이 뚫린 부분은 0, 그렇지 않으면 1
-- 한번에 만들수 있는 아이스크림의 개수는?
'''
# dfs를 사용할 수 있다.
def dfs(x, y):
# 주어진 범위를 벗어나면 False처리로 즉시 종료
if x <= -1 or x>= n or y <=-1 or y>=m:
return False
# 현재 노드를 아직 방문하지 않았다면 = 0
if graph[x][y] == 0:
#해당 노드를 방문처리 1
graph[x][y] = 1
# 상 하 좌 우 의 위치들 모두 재귀적으로 호출
dfs(x -1, y)
dfs(x, y-1)
dfs(x+1, y)
dfs(x, y+1)
return True
return False
# n,m을 공백을 기준으로 구분하여 입력받기
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for i in range(n):
# input으로 입력을 받으면 int형으로 변환하여 매핑하면 graph에 정수형 리스트로 append할 수 있다.
graph.append(list(map(int, input())))
#모든 노드위치에 대하여 음료수 채우기
result = 0
for i in range(n):
for j in range(m):
# n*m의 현재 위치에서 dfs 수행
if dfs(i, j) == True: #dfs수행했을때 방문된것이라면 count+1해준다.
result +=1
print(result)
'''
input
4 5
00110
00011
11111
00000
result = 3
'''
예시문제 2.
'''미로 탈출
n>= 4, m <=200
현재 위치는 (1, 1) 출구는 (n, m)위치
이동할 수 있는 칸수는 한번에 한칸씩
괴물이 있는 부분은 0, 없는 부분은 1
탈출 하기 위한 최소칸의 개수( 칸을 셀때 시작 칸과 마지막 칸을 모두 포함해서 계산)
시작과 마지막 노드는 모두 1 '''
# bfs를 사용하여 풀이
from collections import deque
def bfs(x, y):
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]
n, m = map(int,input().split())
graph = []
for i in range(n):
graph.append(list(map(int, input())))
# 이동할 네가지 방향 정의 (상, 하, 좌, 우)
dx = [-1, 1, 0, 0]
dy = [0, 0, -1,1]
print(bfs(0,0))
'''
input
5 6
101010
111111
000001
111111
111111
result
10
'''
2021.10.11 - [파이썬] - [Computer Science] 자료구조 - 그래프
ref.