root 노드에서 시작해서 다음 분기로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 알고리즘이다
깊이 우선 탐색은 스택 자료구조나 재귀를 사용한다
깊이 우선 탐색은 무방향 그래프에서 사용한다
간단하게 보면, root노드부터 시작해서 출력하고 방문체크를 하는 과정을 한다.
현재 노드의 인접한 노드들 중 방문되지 않은 노드가 있다면 그 노드쪽으로 들어가는 것

해당 노드들이 방문되었는지를 확인하기 위한 방문배열 생성
-> 방문배열이 없으면 재귀를 탈출하지 못하고 무한루프에 빠질 수 있다
그래프들의 정점과 간선을 담아줄 vector배열을 생성
생성자에 방문배열을 모두 초기화한다
#include <iostream>
#include <vector>
#define SIZE 8
using namespace std;
class Graph
{
private:
bool visited[SIZE]; // 방문 배열
vector<int> graph[SIZE]; // 정점의 집합 (그래프)
public:
Graph()
{
// 방문 배열 초기화
for (int i = 0; i < SIZE; i++)
{
visited[i] = NULL;
}
}
여기서 방문배열은 해당 정점이 방문되었는지 아닌지를 체크하기 위한 (T/F) 판단용이다
-> 정점의 숫자와 방문배열의 인덱스를 같게 하는게 일반적이다
-> ex) 정점1이 방문되었다면 visited[1]에 True
-> 아래의 코드에서는 SIZE값이 8이고 정점이 7이기 때문에 [0]인덱스는 사용하지 않고있음
vector <int graph[SIZE]는 크기가 SIZE인 배열을 선언한 것이다
-> 각 인덱스마다 vector<int가 가 들어있고 각 인덱스는 정점을 의미한다
-> 정점에 push_back을 사용해서 벡터에 연결된 간선들(연결된 다른 정점들)을 저장한다


// 정점에 간선들 연결해주기
// 해당 인덱스(정점)마다 연결된 간선들
// (연결된 다른 정점들)을 저장할 수 있다
void Insert(int vertex, int edge)
{
// 무방향 그래프
graph[vertex].push_back(edge);
graph[edge].push_back(vertex);
}
각각의 정점들은 원소 수와 동일하게 [1] ~[7]이다
인자로 들어오는 vertex는 정점의 인덱스 번호
edge는 내가 간선으로 연결할 정점
-> 간선을 연결시켜주면 각각의 정점 내에 들어있는 벡터배열에 연결된 정점이 저장되는 것
-> 무방향 그래프이기 때문에 반대 정점에서도 연결시켜줘야한다

무방향 그래프와 양방향 그래프의 차이점이 뭘까?
- 무방향 그래프 : 간선의 방향이 없다
A -> B 와 B -> A가 동일한 간선으로 취급된다- 양방향 그래프 : 한 간선이 두 정점을 서로 연결할 때, 두 정점 사이 이동이 양방향으로 가능하다
-> 즉, 양방향 연결은 무방향 그래프의 특성으로 각 간선의 성질을 의미한다
1. 재귀함수 사용
// 깊이 우선 탐색 (재귀 사용)
void Search(int start)
{
visited[start] = true;
cout << start << " ";
for (int i = 0; i < graph[start].size(); i++)
{
int next = graph[start][i];
if (visited[next] == false)
{
Search(next);
}
}
}
start부터 방문한 뒤 방문체크하기
해당 정점의 값을 출력하기
해당 정점과 연결된 다른 정점들을 찾아서 방문하지 않은 정점이라면 그 정점으로 재귀함수 호출
재귀를 진행하면서 방문체크가 다 되어 더이상 방문되지 않은 정점이 없을 경우, 함수도 종료되고 이전 함수로 이동한다
-> 계속 진행하다보면 이전에 호출한 함수가 아직 반복문 로직이 다 완료되지 않았었지만, 이미 앞에 재귀로 호출한 함수에서 방문체크가 되어버려 따로 재귀를 또 호출 할 필요가 없어지고 종료된다
-> 연결된 정점이 이전에 재귀로 호출하여 방문체크가 되었다면 조건문이 만족하지 않아 그냥 건너뛰고 다른 연결된 정점을 확인한다



재귀함수 호출 순서 (출력 순서)
-> 함수를 호출하면 방문체크한 뒤 바로 출력부터 되기 때문에 출력 순서와 같음
1 -> 2 -> 3 -> 6 -> 7 -> 4 -> 5
함수 종료 순서
7 -> 6 -> 3 -> 5 -> 4 -> 2 -> 1
#include <iostream>
#include <vector>
#define SIZE 8
using namespace std;
class Graph
{
private:
bool visited[SIZE]; // 방문 배열
vector<int> graph[SIZE]; // 정점의 집합 (그래프)
public:
Graph()
{
// 방문 배열 초기화
for (int i = 0; i < SIZE; i++)
{
visited[i] = NULL;
}
}
// 정점에 간선들 연결해주기
// 해당 인덱스(정점)마다 연결된 간선들(연결된 다른 정점들)을 저장할 수 있다
void Insert(int vertex, int edge)
{
// 무방향 그래프
graph[vertex].push_back(edge);
graph[edge].push_back(vertex);
}
// 깊이 우선 탐색 (재귀 사용)
void Search(int start)
{
// 현재 노드를 방문한 것으로 표시합니다.
visited[start] = true;
// 현재 노드를 출력합니다.
cout << start << " ";
// 현재 노드와 연결된 다른 노드를 재귀적으로 방문합니다.
for (int i = 0; i < graph[start].size(); i++)
{
// 현재 노드와 연결된 다음 노드를 가져옵니다.
int next = graph[start][i];
if (visited[next] == false)
{
// 다음 노드가 방문하지 않은 노드라면 'Search'함수를 호출합니다.
Search(next);
}
}
}
};
int main()
{
#pragma region 깊이 우선 탐색 (Depth First Search)
// root 노드에서 시작해서 다음 분기로 넘어가기 전에
// 해당 분기를 완벽하게 탐색하는 방법입니다.
// 깊이 우선 탐색은 스택 자료 구조를 사용합니다.
// 1. 시작 노드를 스택에 넣고 방문 처리를 합니다.
// 2. 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면
// 그 노드를 스택에 넣고 방문 처리합니다.
// 3. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼냅니다.
// 4. 더 이상 2번의 과정을 수행할 수 없을 때까지 반복합니다.
Graph graph;
graph.Insert(1, 2);
graph.Insert(1, 3);
graph.Insert(2, 3);
graph.Insert(2, 4);
graph.Insert(2, 5);
graph.Insert(3, 6);
graph.Insert(3, 7);
graph.Insert(4, 5);
graph.Insert(6, 7);
graph.Search(1);
#pragma endregion
return 0;
}
출력값 :
2. 스택 자료구조 사용
1. 재귀와 비슷하게 진행되는데, start정점을 스택에 넣고 방문체크를 한다
시간복잡도 : O(V + E)
-> 그래프의 모든 정점과 간선을 한 번씩만 처리하기 때문
공간복잡도 : O(V)
-> 각 정점에 대한 정보를 저장하기 때문
#include <iostream>
#include <vector>
#define SIZE 8 // 정점의 갯수 7 + 1
using namespace std;
// 깊이 우선 탐색 DFS
// 무방향 그래프에서 사용된다
// 방문체크 한 뒤 인접한 노드들 중 방문하지 않은 노드를 탐색한다
// 재귀를 사용 & 들어갔다가 다시 되돌아와서 탐색한다 (백트래킹느낌?)
// 재귀로써 함수가 종료되면 이전에 호출한 함수로 돌아온다
class Graph
{
private:
bool visited[SIZE]; // 방문 배열
// SIZE 크기의 vector<int> 배열
vector <int> graph[SIZE]; // 그래프 인접리스트
public:
Graph()
{
for (int i = 0; i < SIZE; i++)
{
visited[i] = false;
}
}
// 그래프 정점 연결
void Insert(int x, int y)
{
graph[x].push_back(y);
graph[y].push_back(x);
}
void Search(int start)
{
// 1. 방문체크
visited[start] = true;
cout << start << " ";
// 2. 해당 정점과 연결된 정점들 탐색
for (int i = 0; i < graph[start].size(); i++)
{
int next = graph[start][i];
if (visited[next] == false)
{
Search(next);
}
}
}
};
int main()
{
Graph graph;
graph.Insert(1, 2);
graph.Insert(1, 3);
graph.Insert(2, 3);
graph.Insert(2, 4);
graph.Insert(2, 5);
graph.Insert(4, 5);
graph.Insert(3, 6);
graph.Insert(3, 7);
graph.Insert(6, 7);
graph.Search(1);
return 0;
}
결과값: