알고리즘을 풀이할 때 그래프를 탐색할 때 사용하는 방법이다.
공통적으로 이 두 방법은 모두 모든 노드를 방문하는 목적으로 사용된다.
간단하게 용어를 정의하고 가려고 한다.
그래프에 대해서 이해하기 위해서 참조한 개발자 소들이 블로그 이다.(자주 도움이 되는 곳이라 꾸준히 방문하게 되는 것 같다.)
'개발자 소들이님의 블로그'의 예시를 통해서 사용하였습니다.
func DFS(graph: [String: [String]], start: String) -> [String] {
var visitedQueue: [String] = []
var needVisitStack: [String] = [start]
while !needVisitStack.isEmpty {
let node: String = needVisitStack.removeLast()
if visitedQueue.contains(node) { continue }
visitedQueue.append(node)
needVisitStack += graph[node] ?? []
}
return visitedQueue
}
처음에는 방문해야할 노드에만 A가 존재한다.
방문한 노드: []
방문해야할 노드: ["A"]
A는 이미 방문을 하였고, 인접 리스트를 통해서 앞으로 방문해야할 노드는 B와 C가 있다.
방문한 노드: ["A"]
방문해야할 노드: ["B", "C"]
DFS의 경우 Stack으로 구성되어있기 때문에, LIFO 마지막으로 들어온 값을 먼저 추출하게된다. 그리하여 C의 노드를 방문하고, 앞으로 방문해야할 노드는 B와 C의 인접 노드인 A와 F가 들어오게 된다.
이 때, 순서는 상관이 없기 때문에 C가 아닌 B가 먼저 탐색할 수도 있다. 대신 그때 2단계에서 방문해야할 노드가 ["C", "B"]
의 형태로 저장되어 있어야한다.
방문한 노드: ["A", "C"]
방문해야할 노드: ["B", "A", "F"]
위와 동일하게 방문해야할 노드에서 마지막 F를 탐색하고, 인접 노드인 C를 방문해야할 노드에 추가한다.
방문한 노드: ["A", "C", "F"]
방문해야할 노드: ["B", "A", "C"]
4단계에서 방문해야할 노드를 확인하였을 때, 앞으로 방문해야할 노드는 C가 된다. 하지만 이는 이미 방문한 노드가 된다. 그리하여 이때의 노드는 방문한 노드에 추가되지 않는다.
한번 더 진행하더라도 A의 노드는 이미 방문이 완료되었기 때문에 추가되지 않고 다음 단계로 넘어가게된다.
방문한 노드: ["A", "C", "F"]
방문해야할 노드: ["B", "A"]
방문한 노드: ["A", "C", "F"]
방문해야할 노드: ["B"]
5단계에서 방문해야할 노드 B를 방문하면서 인접 노드 A, D, E가 방문해야할 노드에 추가가 된다. 그리고 다음 작업을 진행하게되면 마지막 노드인 E를 방문과 동시에 방문해야할 노드에 B가 추가되고, 이는 이미 방문하였기에 제외된다.
마지막으로 D를 방문한 후 방문해야할 노드에 남은 노드들은 이미 방문을 했었던 곳이다. 그리하여 단계가 진행되더라도 방문한 노드에는 추가되지 않고, 방문해야할 노드에서 사라진다.
결과적으로 방문해야할 노드가 비어있게 된다면 모든 과정이 끝이난다.
방문한 노드: ["A", "C", "F", "B"]
방문해야할 노드: ["A", "D", "E"]
방문한 노드: ["A", "C", "F", "B", "E"]
방문해야할 노드: ["A", "D", "B"]
방문한 노드: ["A", "C", "F", "B", "E"]
방문해야할 노드: ["A", "D"]
방문한 노드: ["A", "C", "F", "B", "E", "D"]
방문해야할 노드: ["A", "B"]
방문한 노드: ["A", "C", "F", "B", "E", "D"]
방문해야할 노드: ["A"]
방문한 노드: ["A", "C", "F", "B", "E", "D"]
방문해야할 노드: []
func BFS(graph: [String: [String]], start: String) -> [String] {
var visitedQueue: [String] = []
var needVisitQueue: [String] = [start]
while !needVisitQueue.isEmpty {
let node: String = needVisitQueue.removeFirst()
if visitedQueue.contains(node) { continue }
visitedQueue.append(node)
needVisitQueue += graph[node] ?? []
}
return visitedQueue
}
처음에는 방문해야할 노드에만 A가 존재한다.
방문한 노드: []
방문해야할 노드: ["A"]
A는 이미 방문을 하였고, 인접 리스트를 통해서 앞으로 방문해야할 노드는 B와 C가 있다.
방문한 노드: ["A"]
방문해야할 노드: ["B", "C"]
DFS의 경우 Queue으로 구성되어있기 때문에, FIFO 첫번째로 들어온 값을 먼저 추출하게된다. 그리하여 B의 노드를 방문하고, 앞으로 방문해야할 노드는 C와 B의 인접 노드인 A와 D, E가 들어오게 된다.
방문한 노드: ["A", "B"]
방문해야할 노드: ["C", "A", "D", "E"]
위와 동일하게 방문해야할 노드에서 첫번째 C를 탐색하고, 인접 노드인 A와 F를 방문해야할 노드에 추가한다.
방문한 노드: ["A", "B", "C"]
방문해야할 노드: ["A", "D", "E", "A", "F"]
4단계에서 방문해야할 노드를 확인하였을 때, 앞으로 방문해야할 노드는 A가 된다. 하지만 이는 이미 방문한 노드가 된다. 그리하여 이때의 노드는 방문한 노드에 추가되지 않는다.
방문한 노드: ["A", "B", "C"]
방문해야할 노드: ["D", "E", "A", "F"]
4단계와 5단계에서 했었던 방법을 참조하여 방문해야할 노드가 없어질 때까지 진행한다.
방문한 노드: ["A", "B", "C", "D"]
방문해야할 노드: ["E", "A", "F", "B"]
방문한 노드: ["A", "B", "C", "D", "E"]
방문해야할 노드: ["A", "F", "B", "B"]
방문한 노드: ["A", "B", "C", "D", "E"]
방문해야할 노드: ["F", "B", "B"]
방문한 노드: ["A", "B", "C", "D", "E", "F"]
방문해야할 노드: ["B", "B", "C"]
방문한 노드: ["A", "B", "C", "D", "E", "F"]
방문해야할 노드: ["B", "C"]
방문한 노드: ["A", "B", "C", "D", "E", "F"]
방문해야할 노드: ["C"]
방문한 노드: ["A", "B", "C", "D", "E", "F"]
방문해야할 노드: []