📃 그래프 탐색에서 사용되는 BFS
와 DFS
알고리즘에 대해 공부하며 정리한 글입니다.
간선(Edge)
로 연결되어 있는 각 정점(Node)
들의 구조를 말한다.
어떤 한 그래프와 해당 그래프의 시작 노드가 주어졌을 때, 시작 점에서 간선을 타고 이동할 수 있는 모든 노드들을 탐색하는 문제를 의미한다.
이 때 그래프 탐색 문제
를 해결하기 위해 BFS
와 DFS
를 이용할 수 있다.
* 너비 우선 탐색 ( Breadth-First Search )
BFS
는 너비 우선 탐색
으로 불리며 이름에 맞게 그래프의 시작점에서 너비가 가까운 노드를 우선적으로 탐색한다.
이와 같이 너비가 가까운 노드( 인접한 노드 )를 우선적으로 탐색하며 진행하는 그래프 탐색 방식을 너비 우선 탐색 ( BFS ) 라고 한다.
BFS를 진행하기 위해서 Queue
자료 구조를 사용할 수 있다.
Queue
를 사용하는 이유는 먼저 탐색한 노드를 먼저 빼내어 해당 노드와 인접한 노드를 탐색한다는 특성 때문이다. 이러한 FIFO
특성에 맞는 자료 구조 중 가장 간단하고 빠른 Queue
를 사용하는 것이다.
1
을 Queue
에 넣고, 방문처리를 한다.1
을 꺼내고 이와 인접한 2
3
8
을 Queue
에 넣고 방문 처리 한다.Queue
에서 2
을 꺼내고 방문하지 않은 7
번 노드를 방문 처리 후 Queue
에 넣는다.Queue
에서 3
을 꺼내고 방문하지 않은 4
5
번 노드를 방문 처리 후 Queue
에 넣는다.Queue
에서 더 이상 꺼낼 노드가 존재하지 않을 때 까지 반복한다.// BFS 함수 정의
// 연결 된 node의 정보를 담고 있는 graph, 시작 점, 방문 기록을 담고 있는 visited
void BFS(vector<vector<int>> graph, int start, vector<bool> visited)
{
// 사용할 queue 정의
queue<int> q;
// 시작 점 queue에 삽입
q.push(start);
// 시작 점 방문 처리
visited[start] = true;
// queue가 빌 때까지 반복
while(q.empty() == false)
{
// 현재 노드를 뽑아오고 queue에서 제거
int node = q.front();
q.pop();
// 인접한 노드들을 탐색
for(int i = 0; i < graph[node].size(); ++i)
{
// 이미 방문한 node인지 체크
if(visited[graph[node][i]] == true)
continue;
// 방문 처리 후 queue에 삽입
visited[graph[node][i]] = true;
q.push(graph[node][i]);
}
}
}
* 깊이 우선 탐색 ( Depth-First Search )
DFS
는 깊이 우선 탐색
으로 불리며 그래프에서 깊은 부분을 우선적으로 탐색한다.
이와 같이 깊은 부분( 연결 된 간선을 모두 탐색 )을 우선적으로 탐색하며 진행하는 그래프 탐색 방식을 깊이 우선 탐색( DFS ) 라고 한다.
1) Stack 자료 구조 사용
마지막으로 방문한 노드와 연결 된 노드를 우선시하여 탐색을 진행하기에 LIFO
구조를 사용하는 자료구조 중 가장 간결하고 빠른 Stack
자료 구조를 사용할 수 있다.
2) 재귀함수 사용
재귀함수란 자기 자신을 다시 호출하는 함수를 말한다.
재귀 함수를 사용할 때는 종료조건을 정해준 후 해당 종료조건이 되기 전까지 함수를 무한히 실행하는데 이러한 조건으로 DFS
를 해결하는데에 사용할 수 있다.
1) Stack 자료 구조 사용
1
을 Stack
에 넣고, 방문처리를 한다.Stack
최상단 1
번 노드와 미방문한 노드 중 가장 작은 2
를 Stack
에 넣고 방문처리한다.Stack
최상단 2
번 노드와 미방문한 노드( 7
)을 Stack
에 넣고 방문처리 한다.Stack
최상단 7
번 노드와 미방문한 노드 중 가장 작은 6
을 Stack
에 넣고 방문처리한다.Stack
최상단 6
번 노드와 미방문한 노드가 없을 시에는 Stack
에서 제거한다.Stack
최상단 7
번 노드와 미방문한 노드 ( 8
)을 Stack
에 넣고 방문처리 한다.Stack
에 값이 없을 때까지 반복한다.2) 재귀함수 사용
1
번 노드와 미방문한 노드 중 가장 작은 2
번 노드를 찾는다.2
번 노드와 미방문한 노드( 7
)를 찾는다.7
번 노드와 미방문한 노드 중 가장 작은 6
번 노드를 찾는다.6
번 노드와 미방문한 노드가 없을 경우 재귀를 종료한다.// Stack을 사용하는 DFS
// 연결 된 node의 정보를 담고 있는 graph, 시작 점, 방문 기록을 담고 있는 visited
void DFS_Stack(vector<vector<int>> graph, int start, vector<bool> visited)
{
// 사용 할 Stack 정의
Stack<int> st;
// Stack에 시작 점 삽입
st.push(start);
// 시작 점 방문처리
visited[start] = true;
// Stack이 빌 때까지 반복
while(st.empty() == false)
{
// 현재 Stack에 최상위 값을 뽑고 Stack에서 제거
int node = st.top();
st.pop();
// 인접한 노드를 탐색
for(int i = 0; i < graph[node].size(); ++i)
{
// 인접한 노드를 찾아주기
int next = graph[node][i];
// 이미 방문했는지 체크
if(visited[next] == true)
continue;
// 방문처리
visited[next] = true;
// pop을 해주었기 때문에 원본 node도 같이 Stack에 넣어준다.
st.push(node);
st.push(next);
break;
}
}
}
//재귀를 사용하는 DFS
// 연결 된 node의 정보를 담고 있는 graph, 현재 노드, 방문 기록을 담고 있는 visited
void DFS_Recursive(vector<vector<int>> graph, int v, vector<bool> visited)
{
// 현재 노드 방문처리
visited[v] = true;
// 인접한 노드 체크
for(int i = 0; i < graph[v].size(); ++i)
{
// 방문하지 않은 노드인지 체크
if(visited[graph[v][i]] == false)
{
// 재귀 실행
DFS_Recursive(graph, i, visited);
}
}
}
좋은 포스팅 감사합니다 ^^
덕분에 많은 정보 얻어 가요 ^^