본문 바로가기
자료구조

[자료구조] DFS와 BFS

by 송파감자 2024. 8. 2.


DFS 와 BFS란?

  • DFS (Depth-First Search)와 BFS (Breadth-First Search)는 그래프 탐색 알고리즘
  • 그래프의 모든 노드를 탐색하는 방법임
  • 두 알고리즘은 탐색의 순서와 방식에서 차이 있음
  • 시간복잡도는 DFS/ BFS 동일함
    • -> 왜냐? 모든 조건 내 모든 노드를 검색하니까
    • --> But, 최단거리 구할 때는 BFS가 유리함

 


1. DFS (Depth-First Search)

- 개념: 

  • DFS는 깊이 우선 탐색
  • 한 노드에서 시작하여 다음 분기로 넘어가기 전에!!
  • 해당 분기를 따라 최대한 깊이 내려가며 탐색하는 방법

 

- 특징: 

  • 스택 (Stack) 사용
  • 재귀 호출을 사용할 수 있음
  • 한 경로를 끝까지 탐색한 후, 다른 경로를 탐색

 

2. BFS (Breadth-First Search)

- 개념:

  • BFS는 너비 우선 탐색
  • 노드에서 시작하여 인접한 모든 노드 먼저 탐색
  • 이후 각 인접 노드에 대해 다시 인접한 노드들을 탐색하는 방식

 

-특징: 

  • (Queue)  사용
  • 현재 노드의 모든 인접 노드들 탐색 후, 다음 인접 노드로 넘어감
#include <iostream>
#include <vector>
using namespace std;

/* 
함수: DFS 
매개변수: 2차원벡터 그래프, 방문 상태 표시하는 벡터, 현재 노드
*/
void DFS(vector<vector<int>> &Graph, vector<bool> &Visited, int Node) 
{
    Visited[Node] = true; // 현재 노드 방문했다고 표시
    cout << Node << " "; 
    
    // 현재 노드에 연결된 모든 노드 확인
    for (int i = 0; i < Graph[Node].size(); ++i) 
    {
        int Next = Graph[Node][i]; // 다음 방문할 노드
        
        if (!Visited[Next]) //만약 다음 노드 방문하지 않았다면
        {
            DFS(Graph, Visited, Next); // 다음 노드에 대해 DFS 재귀 호출
        }
    }
}

int main() 
{
    int NumNodes = 4; // 그래프의 노드 수를 정의
    
    vector<vector<int>> Graph(NumNodes); // 그래프를 인접 리스트 형태로 초기화

    Graph[0] = {1, 2}; // 그래프의 인접 리스트 정의
    Graph[1] = {0, 2};
    Graph[2] = {0, 1, 3};
    Graph[3] = {2};
    
    vector<bool> Visited(NumNodes, false); // 노드의 방문 상태를 저장하는 벡터 초기화

    cout << "DFS starting from node 0: "; // DFS 탐색 시작 노드를 출력

    DFS(Graph, Visited, 0);     // 노드 0부터 DFS 탐색 시작
    cout << endl;

    return 0;
}

 

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

/*
함수 : BFS 너비우선탐색
매개변수 : 그래프의 인접 리스트, 방문 여부 저장 벡터, 시작 노드
*/
void BFS(vector<vector<int>>& Graph, vector<bool>& Visited, int Start) 
{
    queue<int> Q;
    Visited[Start] = true;
    Q.push(Start); // 시작 노드, 큐에 추가

    while (!Q.empty())  // 큐가 비어있지 않는 동안 반복
    {
        int Node = Q.front(); // 큐 앞에 있는 노드 가져옴
        Q.pop();			  // 큐 앞에 있는 노드 Pop
        cout << Node << " ";

        for (int i = 0; i < Graph[Node].size(); ++i)  // 현재 노드에 연결된 모든 노드 확인
        {
            int Next = Graph[Node][i];
            if (!Visited[Next]) // 다음 노드 방문X라면
            {
                Visited[Next] = true; // 다음 노드 방문했다고 표시
                Q.push(Next); // 다음 노드 큐에 추가
            }
        }
    }
}

int main() 
{
    int NumNodes = 4;
    
    vector<vector<int>> Graph(NumNodes);
    Graph[0] = {1, 2};
    Graph[1] = {0, 2};
    Graph[2] = {0, 1, 3};
    Graph[3] = {2};

    vector<bool> Visited(NumNodes, false);
    

    cout << "BFS starting from node 0: ";
    
    BFS(Graph, Visited, 0);
    
    cout << endl;

    return 0;
}

3. 참고 개념

3.1. 스택

-개념:

  • LIFO (Last In, First Out) 구조
  • 마지막에 삽입된 데이터가 가장 먼저 꺼내지는 방식
  • 예시) 접시 쌓기 ->가장 나중에 올려놓은 접시를 가장 먼저 꺼냄

 

-스택의 주요 연산:

  • push: 스택 맨 위에 데이터를 추가
  • pop: 스택 맨 위에 있는 데이터를 제거하고 반환
  • top : 스택의 맨 위에 있는 데이터를 제거하지 않고 반환
  • isEmpty: 스택 비어 있는지 확인

 

3.2. 큐 (Queue)

-개념: 

  • 큐는 FIFO (First In, First Out) 구조
  • 먼저 삽입된 데이터가 먼저 꺼내지는 방식
  • 예시) 줄 서기-> 먼저 줄을 선 사람이 먼저 서비스를 받음

 

-큐의 주요 연산:

  • enqueue (or push): 큐의 뒤에 데이터를 추가
  • dequeue (or pop): 큐의 앞에 있는 데이터를 제거하고 반환
  • front: 큐의 앞에 있는 데이터를 제거하지 않고 반환
  • isEmpty: 큐가 비어 있는지 확인

 

3.3. 재귀함수

-개념 : 

  • 자기 자신을 호출하는 방식으로 문제를 해결하는 프로그래밍 기법
  • 재귀는 주로 복잡한 문제를 더 작은 부분 문제로 나누어 해결할 때 사용

 

- 재귀 함수의 구성 2가지:

  1. 기저 조건 (Base case):
    • 재귀 호출을 멈추는 조건
    • Base Case를 만족하면 함수는 더 이상 자기 자신을 호출하지 않고 종료
  2. 재귀 호출 (Recursive call):
    • 함수가 자기 자신을 호출하여 문제를 더 작은 부분으로 나누어 해결

 

-참고 : 

  • 재귀함수에서 기저조건이 없으면 스택오버플로우 발생
  • 스택오버플로우는 callStack의 한계를 초과할 때 발생하는 런타임 오류

 

예시) 팩토리얼 계산

#include <iostream>
using namespace std;

int Factorial(int Num) 
{
    if (Num == 0)  // 기저 조건
        return 1;
    else 
        return n * factorial(n - 1);
}

int main() 
{
    int Number;

	cout << "Enter a positive integer: ";
    cin >> Number;

    cout << "Factorial of " << Number << " is " << Factorial(Number) << endl;

    return 0;
}

4.  개념 이해하기 쉽게 정리한 블로그 링크 :

https://velog.io/@lucky-korma/DFS-BFS%EC%9D%98-%EC%84%A4%EB%AA%85-%EC%B0%A8%EC%9D%B4%EC%A0%90

'자료구조' 카테고리의 다른 글

[자료구조] Double Linked List (C++)  (1) 2023.10.23
[자료구조] Single Linked List (C++)  (0) 2023.10.15
[자료구조] Stack 구현 (C++)  (3) 2023.03.19
[자료구조] Stack 개념 정리  (2) 2023.03.18