트리 문제와 마찬가지로, 많은 그래프 문제에서도 DFS(깊이 우선 탐색)이나 BFS(너비 우선 탐색) 중 어느 것을 사용해도 상관없는 경우가 많습니다. DFS가 BFS 보다 더 나은 성능을 보이는 상황은 드뭅니다. 사람들이 DFS를 선택하는 주된 이유는 DFS가 특히 재귀적으로 구현할 때 더 빠르고 코드가 간결하기 때문입니다.
그러나, 특정 문제에서는 BFS가 더 적합한 경우가 있습니다. 트리에서는 레벨(Level)에 관련된 문제에서 BFS가 유리했듯, 그래프에서는 최단 경로 를 찾는 문제에서 BFS가 더 적합합니다.
BFS는 시작점에서의 거리에 따라 노드를 방문합니다. 트리에서는 BFS가 특정 깊이(d)에 속한 모든 노드를 방문한 후, 그 다음 깊이(d+1)의 노드를 방문합니다. 이와 동일한 로직이 그래프에도 적용됩니다. BFS는 그래프에서도 시작점에서 특정 노드까지 도달하는 최소 단계를 보장합니다.
n x n 크기의 이진 행렬 grid가 주어집니다.
- (x, y) 좌표와 현재까지의 단계 step 를 저장합니다.
- 초기 상태로 (0, 0, 1) 을 큐에 추가합니다. 여기서 1은 첫번째 단계.
data class State(val row: Int, val col: Int, val steps: Int)
class Solution {
private lateinit var grid: Array<IntArray>
private val directions = arrayOf(
intArrayOf(-1, -1), intArrayOf(-1, 0), intArrayOf(-1, 1),
intArrayOf(0, -1), intArrayOf(0, 1),
intArrayOf(1, -1), intArrayOf(1, 0), intArrayOf(1, 1)
)
fun shortestPathBinaryMatrix(grid: Array<IntArray>): Int {
if (grid[0][0] == 1) return -1 // 시작 지점이 장애물일 경우 -1 반환
val n = grid.size
val seen = Array(n) { BooleanArray(n) } // 방문 여부를 저장하는 배열
val queue: Queue<State> = LinkedList() // BFS 큐
queue.add(State(0, 0, 1)) // 시작점 추가
seen[0][0] = true // 시작점 방문 표시
while (queue.isNotEmpty()) {
val (row, col, steps) = queue.poll()
// 목표 지점 도달 시 현재 단계 반환
if (row == n - 1 && col == n - 1) return steps
// 8방향으로 이동
for (direction in directions) {
val nextRow = row + direction[0]
val nextCol = col + direction[1]
// 유효한 위치인지 확인
if (isValid(nextRow, nextCol, n) && grid[nextRow][nextCol] == 0 && !seen[nextRow][nextCol]) {
seen[nextRow][nextCol] = true // 방문 표시
queue.add(State(nextRow, nextCol, steps + 1)) // 다음 위치 추가
}
}
}
return -1 // 목표에 도달할 수 없는 경우
}
private fun isValid(row: Int, col: Int, n: Int): Boolean {
return row in 0 until n && col in 0 until n
}
}
data class State(val row: Int, val col: Int, val steps: Int)
이동 가능한 8방향(상하좌우 및 대각선)을 정의합니다.
private val directions = arrayOf(
intArrayOf(-1, -1), intArrayOf(-1, 0), intArrayOf(-1, 1),
intArrayOf(0, -1), intArrayOf(0, 1),
intArrayOf(1, -1), intArrayOf(1, 0), intArrayOf(1, 1)
)
val newRow = row + 변화_행
val newCol = col + 변화_열
그리드는 2D 좌표 평면처럼 행(row)와 열(column)로 구성됩니다.
↖ ↑ ↗
← ● →
↙ ↓ ↘
- 가장 왼쪽 위의 셀을 기준으로 잡습니다.
- 즉, **행(row)**은 위에서 아래로 증가하고, **열(column)**은 왼쪽에서 오른쪽으로 증가합니다.
- 위쪽으로 이동 → 행 감소 → -1
- 아래쪽으로 이동 → 행 증가 → +1
- 왼쪽으로 이동 → 열 감소 → -1
- 오른쪽으로 이동 → 열 증가 → +1
이는 컴퓨터 그래픽스와 많은 프로그래밍 언어에서 사용하는 일반적인 2D 좌표계 관습 때문입니다
(0,0) (0,1) (0,2)
(1,0) (1,1) (1,2)
(2,0) (2,1) (2,2)
fun shortestPathBinaryMatrix(grid: Array<IntArray>): Int {
if (grid[0][0] == 1) return -1 // 시작 지점이 장애물일 경우 -1 반환
val n = grid.size
val seen = Array(n) { BooleanArray(n) } // 방문 여부를 저장하는 배열
val queue: Queue<State> = LinkedList() // BFS 큐
queue.add(State(0, 0, 1)) // 시작점 추가
seen[0][0] = true // 시작점 방문 표시
while (queue.isNotEmpty()) {
val (row, col, steps) = queue.poll()
// 목표 지점 도달 시 현재 단계 반환
if (row == n - 1 && col == n - 1) return steps
// 8방향으로 이동
for (direction in directions) {
val nextRow = row + direction[0]
val nextCol = col + direction[1]
// 유효한 위치인지 확인
if (isValid(nextRow, nextCol, n) && grid[nextRow][nextCol] == 0 && !seen[nextRow][nextCol]) {
seen[nextRow][nextCol] = true // 방문 표시
queue.add(State(nextRow, nextCol, steps + 1)) // 다음 위치 추가
}
}
}
return -1 // 목표에 도달할 수 없는 경우
}
초기 검사
- 시작점 (0,0) 이 1(장애물)일 경우, 바로 -1 반환
초기화
- seen 배열 : 방문 여부를 저장
- queue : BFS 큐에 시작점과 초기 단계 수 (1) 을 추가
BFS 탐색
- 큐가 빌 때까지 반복
- 현재 노드를 꺼내고, 8방향으로 탐색
목표 지점 (n-1,n-1)에 도달하면 즉시 steps 반환.
큐가 비었는데도 목표 지점에 도달하지 못하면 -1 반환.
private fun isValid(row: Int, col: Int, n: Int): Boolean {
return row in 0 until n && col in 0 until n
}