[TIL] 알고리즘 BFS & DFS 정리

2025. 9. 23. 14:37·알고리즘

 

알고리즘 문제를 풀다 보면 격자, 네트워크, 지도, 트리 등 연결 구조를 다루는 경우가 많습니다.

이때 필요한 것이 바로 DFS(깊이 우선 탐색)과 BFS(너비 우선 탐색)입니다.


1. DFS (Depth-First Search, 깊이 우선 탐색)

출발점에서 한 방향을 정하면 끝까지 깊게 들어간 뒤, 더 이상 갈 수 없을 때 다시 되돌아와서 다른 경로를 탐색하는 방식입니다.

재귀 함수나 스택(Stack)으로 구현할 수 있습니다.

ex) 미로에 들어간 쥐가 한쪽 길을 끝까지 파고 들어갔다가 막히면 다시 돌아와서 다른 길을 찾는 것과 같습니다.

 

- 장점 : 구현이 간단하고 코드가 짧음

- 단점 : 재귀 호출로 인해 입력 크기가 크면 스택 오버플로우 위험

- 적합한 문제 : 모든 경우를 따져야 하는 문제, 영역 탐색 문제

 

 

 

2. BFS (Breadth-First Search, 너비 우선 탐색)

출발점에서 시작하여 가까운 칸부터 차례로 방문하고, 그다음 레벨로 확장해 나가는 방식입니다.

큐(Queue)를 사용하여 구현합니다.

ex) 물을 한 방울 떨어뜨리면 파동이 퍼지듯 모든 방향으로 동시에 퍼져나가는 것과 같습니다. 

 

- 장점 : 최단 거리/최소 시간문제 해결에 탁월

- 단점 : 구현이 DFS보다 다소 길어질 수 있음

- 적합한 문제 : 최단 거리, 최소 시간 문제

 

3. 구현 방식의 차이

DFS : 깊게 들어가는 성격 → 재귀 호출/스택 사용

BFS : 가까운 곳부터 탐색하는 성격 → 큐 사용

구분 DFS BFS
자료구조 스택(재귀/직접) 큐
탐색 순서 깊이 우선 너비 우선
최단거리 보장 X O
대표 문제 알파벳, 연구소 토마토, 탈출

DFS / BFS

 

4. 수도코드

DFS (재귀)

dfs(r, c):
  방문[r][c] = true
  size = 1
  for 방향 d:
    nr, nc = r+dr[d], c+dc[d]
    if (nr, nc)가 범위 안이고, 방문X이고, map[nr][nc]==1:
       size += DFS(nr, nc)
  return size

 

BFS (큐)

bfs(sr, sc):
  방문[sr][sc] = true
  큐에 (sr, sc) 삽입
  size = 0
  while 큐가 비어있지 않음:
    (r, c) = 큐.pop()
    size += 1
    for 방향 d:
      nr, nc = r+dr[d], c+dc[d]
      if (nr, nc)가 범위 안이고, 방문X이고, map[nr][nc]==1:
         방문[nr][nc] = true
         큐에 (nr, nc) 삽입
  return size

 

5. 예시 문제 풀이

BOJ 1926 그림

https://www.acmicpc.net/problem/1926

 

풀이 과정
1. 이중 for문으로 모든 칸 확인
2. 아직 방문하지 않았고 1이면 새로운 그림 발견
3. DFS 또는 BFS로 그림 크기(size) 탐색
4. 그림 개수 증가, 최대 크기 갱신

 

(1) DFS 풀이

import java.util.*;

public class Main {  
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static int[][] arr;
    static int n, m;
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        arr = new int[n][m];
        int count = 0;
        int max = 0;

        // 각자 방에서 갈 수 있는 값 확인
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                arr[i][j] = sc.nextInt();
            }
        }

        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(arr[i][j] == 1) {
                    int sum = check(i, j);
                    count++;
                    if(max < sum) {
                        max = sum;
                    }
                }
            }
        }
        System.out.println(count);
        System.out.println(max);
    }
    
    static int check(int x, int y) {
        int xx = x;
        int yy = y;
        arr[xx][yy] = 0; // 방문 체크
        int size = 1;
        for(int i = 0; i < 4; i++) {
            int cx = xx + dx[i];
            int cy = yy + dy[i];
            if(cx >= 0 && cy >= 0 && cx < n && cy < m && arr[cx][cy] != 0) {
                arr[cx][cy] = 0;
                size += check(cx, cy);
            }
        }
        return size;
    }
}

 

(2) BFS 풀이

import java.util.*;

public class Main {
	static int N, M;
	static int[][] arr;
	static boolean[][] visited;
	static int[] dx = { 0, 0, -1, 1 };
	static int[] dy = { -1, 1, 0, 0 };

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		M = sc.nextInt();
		
		arr = new int[N][M];
		visited = new boolean[N][M];

		// 0은 색칠이 안된 부분, 1은 색칠이 된 부분
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				arr[i][j] = sc.nextInt();
			}
		}

		int numPictures = 0; // 총 그림의 수
		int maxSize = 0; // 가장 큰 그림의 크기
		
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				if (arr[i][j] == 1 && !visited[i][j]) {
					numPictures++;
					int size = bfs(i, j);
					if (size > maxSize) {
						maxSize = size;
					}
				}
			}
		}
		
		System.out.println(numPictures);
		System.out.println(maxSize);

	}

	static int bfs(int x, int y) {
		Queue<int[]> queue = new ArrayDeque<>();
		queue.add(new int[] { x, y });

		visited[x][y] = true;
		int size = 1; // 현재 그림 크기

		while (!queue.isEmpty()) {
			int[] current = queue.poll();
			int cx = current[0];
			int cy = current[1];

			for (int i = 0; i < 4; i++) {
				int nx = cx + dx[i];
				int ny = cy + dy[i];

				if (nx >= 0 && ny >= 0 && nx < N && ny < M && !visited[nx][ny] && arr[nx][ny] == 1) {
					visited[nx][ny] = true;
					queue.add(new int[] { nx, ny });
					size++;
				}
			}
		}
		return size;
	}
}

 

(3) 성능 비교

DFS, BFS 순

이번 문제에서는 BFS 풀이가 DFS보다 더 빠르게 동작하는 것을 확인했습니다.
하지만 항상 BFS가 DFS보다 빠른 것은 아닙니다.

 

- DFS: 재귀 호출을 사용해 코드가 간단하지만, 깊은 탐색에서는 스택 오버헤드 발생 가능

- BFS: 큐를 사용해 코드가 길어지지만, 재귀 호출이 없어서 대규모 입력에서도 안정적

 

즉, 성능 차이는 입력 크기와 구현 방식(재귀/반복, 자료구조 선택)에 따라 달라집니다.
이번 케이스에서는 BFS가 더 좋은 성능을 보였지만, 다른 상황에서는 DFS가 더 유리할 수도 있습니다.

 

알고리즘 문제를 풀기 앞서
"최단 거리/최소 시간문제냐?" vs "모든 경우/영역 탐색 문제냐?"를 구분하세요 !
그에 따라 DFS와 BFS 중 적절한 방법을 빠르게 선택하는 것이 실전 알고리즘 풀이의 핵심입니다.

 

'알고리즘' 카테고리의 다른 글

알고리즘을 공부하면서 깨달은 점.zip  (1) 2025.11.24
[TIL] 코딩테스트에서 Scanner 대신 BufferedReader를 써야 하는 이유  (1) 2025.09.06
알고리즘 공부 정리  (3) 2025.08.29
조합과 순열 : 개념부터 구현 방식까지 정리  (0) 2025.05.05
자바 문자열 핵심 정리  (0) 2025.03.22
'알고리즘' 카테고리의 다른 글
  • 알고리즘을 공부하면서 깨달은 점.zip
  • [TIL] 코딩테스트에서 Scanner 대신 BufferedReader를 써야 하는 이유
  • 알고리즘 공부 정리
  • 조합과 순열 : 개념부터 구현 방식까지 정리
uoaheu
uoaheu
uoaheu 님의 블로그 입니다.
  • uoaheu
    uoaheu 님의 블로그
    uoaheu
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • 알고리즘 (7)
      • CS (9)
      • FRONTEND (9)
        • React (12)
        • Kotlin (1)
        • JS (5)
        • HTML (2)
      • SQL (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    mysql
    토스 uiux
    BFS
    알고리즘
    부트캠프후기
    toss 분석
    toss uiux
    boj25418
    백준1926번
    토스분석
    mysql 피벗테이블
    useactionstate
    토스 앱 분석
    리액트usestate
    mysql로 피벗테이블만들기
    유레카3기
    이더넷프레임
    멀티캠퍼스it부트캠프
    엘지유플러스유레카프론트엔드
    혼자서 공부하는 네트워크
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
uoaheu
[TIL] 알고리즘 BFS & DFS 정리
상단으로

티스토리툴바