9시 24분
DFS/BFS 본문
'이것이 취업을 위한 코딩 테스트다' 참고
탐색 : 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정
- 그래프, 트리 등의 자료구조 안에서 탐색을 하는 문제를 자주 다룬다.
- 대표적인 탐색 알고리즘 : DFS/BFS
- 스택과 큐에 대한 이햐가 전제되어야 함
자료구조 : 데이터를 표현하고 관리하고 처리하기 위한 구조
- 스택과 큐는 삽입(Push) / 삭제(Pop) 두 핵심적인 함수로 구성
- 오버플로(가득 찬 상태에서 삽입 연산 수행) / 언더플로(전혀 들어 있지 않은 상태에서 삭제 연산 수행)도 고민해주어야 함
스택 : 선입후출 구조
stack = []
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
stack.append(5)
stack.append(2)
stack.append(3)
stack.append(7)
stack.pop()
stack.append(1)
stack.append(4)
stack.pop()
print(stack) # 최하단 원소부터 출력
print(stack[::-1]) # 최상단 원소부터 출력
'''
[5, 2, 3, 1]
[1, 3, 2, 5]
'''
- 별도의 라이브러리 사용할 필요 없음
- append(), pop() 메서드 이용
큐 : 선입선출 구조
from collections import deque
# 큐(Queue) 구현을 위해 deque 라이브러리 사용
queue = deque()
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
queue.append(5)
queue.append(2)
queue.append(3)
queue.append(7)
queue.popleft()
queue.append(1)
queue.append(4)
queue.popleft()
print(queue) # 먼저 들어온 순서대로 출력
queue.reverse() # 다음 출력을 위해 역순으로 바꾸기
print(queue) # 나중에 들어온 원소부터 출력
'''
deque([3, 7, 1, 4])
deque([4, 1, 7, 3])
'''
- collections 모듈의 deque 자료구조를 활용하자
- 데이터를 넣고 빼는 속도가 리스트 자료형에 비해 효율적이며, queue 라이브러리를 이용하는 것보다 더 간단하다.
- deque 객체를 list() 메서드를 이용하여 리스트로 변환해주자
재귀함수 : 자기 자신을 다시 호출하는 함수
- 컴퓨터 내부에서 재귀 함수의 수행은 스택 자료구조를 이용
- 스택 자료구조를 활용해야 하는 상당수 알고리즘은 재귀 함수를 이용해서 간편하게 구현될 수 있음 (ex. DFS)
- 수학의 점화식을 그대로 소스코드로 옮긴 것
- 점화식 : 특정한 함수를 자신보다 더 작은 변수에 대한 함수의 관계로 표현한 것
- 재귀함수 사용 > 코드가 더 간결해짐
- 특정 조건일 때 더 이상 재귀적으로 함수를 호출하지 않고 종료하도록 if문을 이용하여 꼭 종료 조건을 구현해주어야 한다.
그래프 : 노드(=정점)와 간선으로 표현
- 인접 행렬 : 2차원 배열로 그래프의 연결 관계를 표현하는 방식
- 인접 리스트 : 리스트로 그래프의 연결 관계를 표현하는 방식
[ 인접 행렬 방식 예제 ]
- 연결이 되어 있지 않은 노드끼리는 무한의 비용이라고 작성
INF = 999999999 # 무한의 비용 선언
# 2차원 리스트를 이용해 인접 행렬 표현
graph = [
[0, 7, 5],
[7, 0, INF],
[5, INF, 0]
]
print(graph)
[ 인접 리스트 방식 예제 ]
- 연결 리스트라는 자료구조를 이용해 구현 - 기본 자료형의 append() 메소드 이용
# 행(Row)이 3개인 2차원 리스트로 인접 리스트 표현
graph = [[] for _ in range(3)]
# 노드 0에 연결된 노드 정보 저장 (노드, 거리)
graph[0].append((1, 7))
graph[0].append((2, 5))
# 노드 1에 연결된 노드 정보 저장 (노드, 거리)
graph[1].append((0, 7))
# 노드 2에 연결된 노드 정보 저장 (노드, 거리)
graph[2].append((0, 5))
print(graph)
인접 행렬 방식 | 인접 리스트 방식 |
모든 관계 저장 - 노드 개수가 많을수록 메모리가 불필요하게 낭비 | 연결된 정보만을 저장 - 메모리 효율적으로 사용 |
- 인접 리스트 방식은 인접 행렬 방식에 비해 특정한 두 노드가 연결되어 있는지에 대한 정보를 얻는 속도가 느리다.
- 특정한 노드와 연결된 모든 인접 노드를 순회해야 하는 경우, 인접 리스트 방식이 메모리 공간의 낭비가 적다.
DFS, 깊이 우선 탐색
: 그래프에서 깊은 부분을 우선적으로 탐색, 스택 자료구조를 이용
- 데이터의 개수가 N개인 경우 O(N)의 시간 소요
- 재귀 함수를 이용했을 때 간결하게 구현 가능
1) 탐색 시작 노드를 스택에 삽입하고 방문 처리를 한다.
2) 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면 그 인접 노드를 스택에 넣고 방문 처리를 한다.
방문하지 않은 인접 노드가 없으면 최상단 노드를 꺼낸다.
3) 2번의 과정을 더 이상 수행할 수 없을 때까지 반복한다.
( 구체적인 과정을 그림으로 나타낸 것은 책의 137쪽 참고 )
# 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차원 리스트)
graph = [
[],
[2, 3, 8],
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
# 각 노드가 방문된 정보를 리스트 자료형으로 표현(1차원 리스트)
visited = [False] * 9
# 정의된 DFS 함수 호출
dfs(graph, 1, visited)
- dfs 메서드를 따로 정의
- 인접 리스트 방식
BFS, 너비 우선 탐색
: 가까운 노드부터 탐색, DFS의 반대, 큐 자료구조를 이용
- deque 라이브러리 사용
- O(N)의 시간 소요
- 일반적인 경우 DFS보다 수행 시간 좋다
1) 탐색 시작 노드를 큐에 삽입하고 방문 처리를 한다.
2) 큐에서 노드를 꺼내 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리를 한다.
3) 2번의 과정을 더 이상 수행할 수 없을 때까지 반복한다.
( 구체적인 과정은 책 144쪽 참고 )
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 | BFS | |
동작 원리 | 스택 | 큐 |
구현 방법 | 재귀 함수 | 큐 자료구조 |
'알고리즘' 카테고리의 다른 글
프로그래머스 - 짝지어 제거하기 (0) | 2021.09.07 |
---|---|
최대공약수를 구하는 3가지 방법 (0) | 2021.06.25 |
구현 (0) | 2021.06.21 |
그리디 (0) | 2021.06.18 |
파이썬 기본 문법 복습 (0) | 2021.06.18 |