![8. [Java] 그래프 기초 / BFS (너비 우선 탐색)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSrrjJ%2FbtsMo5zL5yT%2FITwV7Hmd8AwNgC3uSk83W0%2Fimg.png)
Graph

- 일반적으로 그래프에서 정점의 표현은 v (Vertex), 간선의 표현은 E (Edge)로 한다.
- 바둑판 형태의 맵에서 탐색하는 것 또한 각 칸을 정점, 다른 칸으로 이동 가능한 방법을 간선으로 볼 수 있다.
- 일반적인 문제에선 정점과 간선의 조합으로 문제가 많이 나오기 떄문에 그패의 정보를 저장할 떄 인접리스트를 많이 사용하게 되지만, 기업 입사 코딩테스트에서는 바둑판 형태의 그래프가 더 자주 나온다.

상하좌우로 이동 가능할 경우 간선은?
- 기본적으로 1칸당 4개의 간선이 있는 것으로 생각
- 범위 밖으로 나가는 경우에 대해서는 예외처리
인접 리스트
그래프의 정보는 코딩테스트에서 어떻게 주어지고, 어떻게 저장을 해야 탐색에 유리할까?
일반적으로 그래프의 정보는 아래와 같이 주어진다.

[A, B] : A에서 B로 이동 가능하다.
[1,2] [2,3] [2,4] [5,2] [5,1] [5,4]
입력값과 동일하게 단순한 리스트로 저장하면, 어떤 정점 X에서 이동 가능한 정점을 탐색하기 위해 O(E)의 시간이 걸린다.
예를 들어, 1번 정점에서 이동 가능한 정점을 탐색하려면, 이 간선 데이터를 전부 탐색해서 [1,X] 인 데이터를 다 찾아내야 한다.
어떤 정점 X에서 연결된 (이동 가능한) 다음 정점을 효율적으로 알아내는 방법으로 인접리스트를 사용한다.
List는 정점의 개수만큼 가진 2차원 배열과 같은 형태이다.
List[정점] = [해당 정점에서 탐색 가능한 정점]
List[1] = [2]
List[2] = [3,4]
List[3] = []
List[4] = []
List[5] = [1,2,4]
위와 같이 만들면 어떤 정점 X가 5일 때,
ArrayList[X]에 [1,2,4]가 담겨있기 때문에 X에서 이동 가능한 정점들을 빠르게 O(1)만에 탐색할 수 있다.
import java.util.ArrayList;
public class test {
private static final int[][] input = {{1, 2}, {2, 3}, {2, 4}, {5, 2}, {5, 1}, {5, 4}};
public static void main(String[] args) {
int N = 5; // 정점의 개수
ArrayList<Integer>[] adjList = new ArrayList[N + 1];
for(int i = 1; i <= N; i++) {
adjList[i] = new ArrayList<>(); // 인접리스트 초기화
}
for (int[] arr : input) {
adjList[arr[0]].add(arr[1]);
// 예시1 :1번 정점에서 이동 가능한 곳은 2번 정점 (1 -> 2)
}
for(int next : adjList[5]) {
System.out.println("next : " + next);
}
}
}
위의 코드는 인접 리스트에서 5번 정점에서 갈 수 있는 다음 정점을 출력하는 예시 코드이다.

ArrayList<Integer>[] adjList = new ArrayList[N + 1]; 는 N + 1 크기의 ArrayList<Integer> 타입의 배열을 생성하는 코드.
이 배열의 각 요소는 ArrayList<Integer> 객체를 가리킬 수 있으며, 이를 통해 인접 리스트와 같은 그래프 구조를 표현할 수 있다.
따라서, adjList는 ArrayList 객체를 담고 있는 1차원 배열이다.
각 ArrayList는 그래프의 각 노드에 연결된 이웃 노드들을 저장하는 데 사용될 수 있다.
- 굳이 ArrayList의 배열로 만드는 이유는 2차원 배열로 arr[V][V]와 같이 데이터를 저장할 경우, 정점의 개수 V가 100,000개일 경우에 메모리 초과 (공간복잡도 V^2)가 발생할 수 있기 때문이다.
ArrayList는 동적 배열이기 때문에 공간복잡도가 O(V+E) 가 된다. - 인접 리스트는 DFS, BFS, 다익스트라 알고리즘과 같은 그래프 문제에서 간혹 필요한 케이스가 있으니, 이렇게 할 수 있다 정도까지만 알아두자. 자주 출제되지는 않는다.
⭐️ BFS (Breadth First Search : 너비 우선 탐색)
- 탐색 시작점에서 가까운 정점들로부터 방문하게 되는 탐색 방법
- 간선의 가중치가 전부 동일한 경우 최단 거리를 구할 수 있다.
- 일반적으로 큐(Queue) 자료구조를 사용하여 구현한다.
- 동일한 정점을 재방문하지 않을 경우, 다음에 방문할 정점을 큐(Queue)에 넣을 때 해당 정점에 대해 방문처리를 한다.
- 방문처리 없이 동일한 정점을 재방문할 경우, 시간 초과가 발생할 수 있다.
📝 BFS (Breadth First Search) 탐색 구현 방법
- 비어 있는 큐(Queue), 방문 배열, 최단 거리(필요한 경우) 배열을 만든다.
- 시작 정점을 큐에 넣고, 방문 배열에 방문했음을 저장한다.
- 큐에 데이터가 남아있을 때까지 아래 과정을 반복한다.
- 큐에서 정점을 꺼낸다.
- 해당 정점에서 방문 가능한 다음 정점을 큐에 넣는다. (이미 방문한 정점은 제외한다.)
- (필요한 경우) 해당 정점이 유효한 정점인지 체크한다.
- (필요한 경우) 해당 정점으로 이동 가능한지 체크한다.
- (필요한 경우) 해당 정점의 최단 거리에서 +1을 한 값을 다음 정점의 최단 거리로 저장한다.
- 큐에 정점을 넣으면서 해당 정점에 대해 방문했음을 저장한다.
바둑판 모양에서 BFS 탐색을 해보자.

상하좌우 4개 방향으로 이동 가능하다고 할 때,
정점 (2,2) 기준에서 보면 이동 가능한 정점은 아래와 같다.
상 (1,2)
하 (3,2)
좌 (2,1)
우 (2,3)
이를 정점 (2,2) 기준에서 보면 아래와 같다
상 (2 + -1, 2 + 0)
하 (2 + 1, 2 + 0)
좌 (2 + 0, 2 + -1)
우 (2 + 0, 2 + 1)
이를 나열하면 [-1, 1, 0, 0], [0, 0, -1, 1] 이 된다.
rx = [-1, 1, 0, 0]
ry = [0, 0, -1, 1]
라고 정의했을 때,
현재 위치를 (now_r, now_c) 라 가정하면, 이동가능한 다음 정점은 아래와 같다.
for (i = 0 -> 4) : // 상하좌우 탐색
now_r = now_r + rx[i];
now_c = now_c + ry[i];

<BFS 예시 코드>
https://school.programmers.co.kr/learn/courses/30/lessons/1844
프로그래머스
SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
기본 템플릿으로 외우면 좋다
import java.util.ArrayDeque;
import java.util.Arrays;
public class BFS_test {
private static final int[] rx = {0, 0, 1, -1};
private static final int[] ry = {1, -1, 0, 0};
private static class Node {
int r, c;
public Node(int r, int c) {
this.r = r;
this.c = c;
}
}
// map이 아래 조건처럼 주어질 때, 최단 거리 구하기
public static void main(String[] args) {
int N = 5;
int M = 5;
int[][] map = new int[N][N];
for (int i = 0; i < N; i++) {
Arrays.fill(map[i], 1); // 모든 배열의 칸을 1로 초기화
}
ArrayDeque<Node> queue = new ArrayDeque<>(); // 비어있는 큐
boolean[][] visited = new boolean[N][N]; // 정점에 대한 방문처리 배열
int[][] dist = new int[N][N]; // 최단거리 배열
queue.addLast(new Node(0, 0)); // 시작 정점을 큐에 넣고,
visited[0][0] = true; // 방문 배열에 방문했음을 저장
// 문제 조건에 맞춰, 이 조건 추가
dist[0][0] = 1; // 없으면, 0
while (!queue.isEmpty()) {
Node now = queue.pollFirst(); // 현재 위치한 정점
for (int i = 0; i < 4; i++) { // 상하좌우 탐색
int nr = now.r + rx[i];
int nc = now.c + ry[i];
if (nr < 0 || nc < 0 || nr >= N || nc >= N) {
continue; // map 밖으로 나가는 경우, 예외처리
}
if (map[nr][nc] == 0) // 갈 수 없는 곳 (벽)
continue;
// 방문하지 않은 정점이라면, 방문
if (!visited[nr][nc]) {
visited[nr][nc] = true; // 방문
queue.addLast(new Node(nr, nc));
dist[nr][nc] = dist[now.r][now.c] + 1; // 다음 정점 최단거리 = 현재 정점 최단거리+1
}
}
}
for (int i = 0; i < N; i++) {
System.out.println(Arrays.toString(dist[i]));
}
System.out.println(dist[N - 1][N - 1]);
}
}

'CS > 알고리즘' 카테고리의 다른 글
12. [Java] 투 포인터 (2-Pointer) (0) | 2025.03.21 |
---|---|
11. [Java] DP (Dynamic Programming : 동적 계획법) (0) | 2025.02.25 |
10. [Java] Backtracking (퇴각 검색) (1) | 2025.02.25 |
9. [Java] DFS (깊이 우선 탐색) (0) | 2025.02.21 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!