말 그대로 모든 경우의 수를 탐색하는 알고리즘이다.
최댓값 혹은 최솟값을 구해야 할 때 주로 사용되는데, 선택 정렬과 어느 정도 비슷한 부분이 있는 것 같다. 완전탐색은 DFS/BFS로 수행하는데, 이때 사용되는 자료구조가 스택과 큐이다. (스택, 큐 대신 재귀로도 구현할 수 있다.)
자, 이제 하나하나 찬찬히 살펴보고 백준 실제 문제 풀이를 통해 완전탐색을 분할 정복 해보겠다. 💪
스택 (FILO)
append()
로 push를, pop()
으로 pop을 하면서 스택을 통한 백트래킹을 구현할 수 있다.print(stack[::-1])
로, (최하단 원소부터는) print(stack)
으로 구현할 수 있다.재귀함수
보통 DFS를 구현할 때 for문 대신 재귀함수를 많이 사용하는 것 같다. (아니, 많이 사용한다.)
재귀함수를 쓸 때는 반드시 유의해야 할 사항이 있다.
종료 조건을 반드시 명시한다. (안 그러면 무한루프)
함수에 대한 데이터는 스택 프레임에 저장되었다가 pop 된다. 그래서 스택을 사용해야 할 때 스택 라이브러리 대신 재귀 함수를 이용하는 경우가 많다고 한다.
아무튼 다시 DFS로 돌아와서,
스택 혹은 재귀함수를 통해 구현한 DFS의 내부 동작 과정은 다음과 같다.
1. 탐색 시작 노드를 스택에 삽입하고 방문 처리를 한다. (번호가 가장 낮은 것부터)
2. 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면, 그 노드를 스택에 넣고 방문 처리 한다. 이때, 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다.
3. 더 이상 2번의 과정을 수행할 수 없을 때까지 반복한다.
이를 코드로 구현해 보면 다음과 같다.
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차원 리스트 -> 총 8개의 노드로 이뤄진 그래프)
graph = [
[], # 0번째 인덱스는 빈칸으로 둔다. (노드1 ~ 부터 시작한다.)
[2, 3, 8], # 1번 노드는 2, 3, 8번 노드와 연결되어 있다.
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
# 각 노드가 방문한 정보를 표현한다. (1차원 리스트)
visited = [False] * 9
dfs(graph, 1, visited)
큐 (FIFO)
append()
로 push를, popleft()
로 pop을 수행한다.큐를 통해 구현한 BFS의 구체적인 동작 과정은 다음과 같다.
1. 탐색 시작 노드를 큐에 삽입하고 방문 처리를 한다.
2. 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리 한다.
3. 더 이상 2번의 과정을 수행할 수 없을 때까지 반복한다.
이를 코드로 구현해 보면 다음과 같다.
from collections import deque
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[v]:
queue.append(i)
visited[i] = True
# 각 노드가 연결된 정보를 표현한다. (2차원 리스트 -> 총 8개의 노드로 이뤄진 그래프)
graph = [
[], # 0번째 인덱스는 빈칸으로 둔다. (노드1 ~ 부터 시작한다.)
[2, 3, 8], # 1번 노드는 2, 3, 8번 노드와 연결되어 있다.
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
# 각 노드가 방문한 정보를 표현한다. (1차원 리스트)
visited = [False] * 9
bfs(graph, 1, visited)
deque
라이브러리appendleft(x)
/ append(x)
-> deque는 모두 O(1)이다. (리스트는 O(n), O(1))leftpop()
/ pop()
-> deque는 모두 O(1)이다. (리스트는 O(n), O(1))