(자세한 개념 설명보다는 구현 위주의 글입니다.)
DFS,BFS 개념 습득은 아래의 영상을 참고하시길 추천드립니다.
https://www.youtube.com/watch?v=_hxFgg7TLZQ
[재귀]
1. 재귀로는 일단 지점 1부터 지점1이 연결되는 끝까지 파고 들어간 다음,
[스택]
1. 스택은 시작 지점을 먼저 삽입하고, 시작지점과 연결된 지점들을 모두 스택에 넣는다.
그 후 가장 마지막에 삽입된 지점부터 꺼내서(pop),
그 지점의 하위 지점을 탐색하고 스택에 넣는다.
그리고 마지막에 삽입 된 지점을 꺼내기 때문에, 2번에 삽입한 지점이 끝까지 탐색하기 전까지는 1번에서 넣었던 나머지 지점을 탐색할 수 없다.
즉, 가장 마지막에 넣은 지점부터 계속 탐색 한다고 보면 된다.
여기서 백트래킹을 잠깐 알고가자.
- 백트래킹은 해결책에 대한 후보를 구축해 나아가다 가능성이 없다고 판단되는 즉시 후보를 포기해 정답을 찾아가는 범용적인 알고리즘으로 제약 충족 문제를 풀 때 주로 쓰인다.
- 탐색을 예로 들면 이 길이 아닌 것 같으면 왔던 길로 되돌아와 다른 선택지로 간다고 생각하면 될 것 같다.
- 백트래킹은 재귀로 보통 구현하는데, 재귀 함수가 호출되고 조건에 맞지 않으면 종료되고 그전에 호출된 재귀로 돌아오므로, 백트래킹에서 말하는 '가능성이 없으면 후보를 포기해 정답을 찾아가는(다시 왔던 길로 돌아가는) 느낌이라고 할 수 있겠다.
- 안 되는 조건은 없애면서 탐색하기 때문에 시간복잡도가 선형적으로 증가할 법한 문제에서 백트래킹을 적용하면 시간복잡도를 줄일 수 있다.
백트래킹을 잘 설명한 글
# 인접간선 설명
graph = {
1: [2,3,4],
2:[5],
3:[5],
4:[],
5:[6,7],
6:[],
7:[3],
}
# 재귀로 구현한 코드
def recursive_dfs(vertex, visited=[]):
visited.append(vertex)
for item in graph[vertex]:
if not item in visited:
visited = recursive__dfs(item, visited)
return visited
# 스택으로 구현한 코드
def stack_dfs(start_vertex):
visited = []
stack = [start_vertex]
while stack:
vertex = stack.pop()
if vertex not in visited:
visited.append(vertex)
for item in graph[vertex]:
stack.append(item)
return visited
지점들(vertex)을 순서대로 큐에 넣고 하나 뽑고(pop)
그 뽑은 지점의 하위 경로를 다시 큐에 넣고
그 다음 지점을 큐에서 뽑고
그 지점의 하위 경로를 다시 큐에 넣고.. 하는 식으로 진행된다.
이렇게 구현하면 먼저 넣은 지점들부터 탐색하고 그 후 그 지점들의 하위 지점들을 탐색하는 식으로 진행 된다.
여기서는 리스트를 썼지만 좀 더 효율적으로 사용하려면 deque 같은 자료형을 써도 좋을 것 같다.
# graph는 위의 리스트 사용
def iterative_bfs(start_vertex):
visited = [start_vertex]
queue = [start_vertex]
while queue:
vertex = queue.pop(0)
for item in graph[vertex]:
if item no in visited:
visited.append(item)
queue.append(item)
return visited
참고로 BFS는 재귀로 동작하지 않는다.
BFS 참고 : https://gmlwjd9405.github.io/2018/08/15/algorithm-bfs.html