幅優先探索 (BFS) で 全部の 部屋から いちばん近い 出口までの 歩数を求める
部屋と
海外で定番の問題リスト 4 本には入っていません
問題
建物の 1 つのgrid で0 (出口) か 1 (部
- 出口は
少なくとも 1 つあります - 斜めには
移れません - 行の
数と 列の 数は、 どちらも 1 以上 100 以下です。 どの 行も 同じ長さです
入力と出力の例
例 1
出口は
例 2
出口が
例 3
出口が
面接での聞かれ方
面接では、
- 「0 と 1 の
行列で、 各マスから いちばん近い 0 までの 距離を 返してください」 - 部屋ごとに
出口を 探す 書き方を 出すと、 「部屋が 1 万あったら 何回探しますか? 逆から 探せませんか?」と 聞かれます - 壁が
あって 通れない マスの ある 版 (部屋と 門の 問題) へ 広げて 聞かれる こともあります。 壁の マスを 列に 入れないだけで、 同じ 書き方で解けます
出口が
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、
関わる学び
グラフ探索は、
まず 考える手順
段 1
部屋ごとに
段 2
逆から
段 3
答えの
段 4
全部の
解答
Python
Python
from collections import deque
STEPS = ((1, 0), (-1, 0),
(0, 1), (0, -1))
def exit_distance(grid):
h = len(grid)
w = len(grid[0])
dist = [[-1] * w
for _ in range(h)]
q = deque()
for r in range(h):
for c in range(w):
if grid[r][c] == 0:
dist[r][c] = 0
q.append((r, c))
while q:
r, c = q.popleft()
d = dist[r][c] + 1
for dy, dx in STEPS:
y = r + dy
x = c + dx
if y < 0 or y >= h:
continue
if x < 0 or x >= w:
continue
if dist[y][x] == -1:
dist[y][x] = d
q.append((y, x))
return distJavaScript
JavaScript
function exitDistance(grid) {
const q = [];
const dist = [];
grid.forEach((row, r) => {
dist.push([]);
row.forEach((v, c) => {
dist[r].push(v ? -1 : 0);
if (v === 0) {
q.push([r, c]);
}
});
});
for (const [r, c] of q) {
const d = dist[r][c] + 1;
for (const [y, x] of [
[r + 1, c], [r - 1, c],
[r, c + 1], [r, c - 1],
]) {
if (dist[y]?.[x] !== -1) {
continue;
}
dist[y][x] = d;
q.push([y, x]);
}
}
return dist;
}Go
Go
var steps = [][2]int{
{1, 0}, {-1, 0},
{0, 1}, {0, -1},
}
func exitDistance(
grid [][]int,
) [][]int {
h := len(grid)
w := len(grid[0])
dist := make([][]int, h)
q := [][2]int{}
for r := range grid {
dist[r] = make([]int, w)
for c := range grid[r] {
dist[r][c] = -1
if grid[r][c] == 0 {
dist[r][c] = 0
q = append(q,
[2]int{r, c})
}
}
}
for i := 0; i < len(q); i++ {
r, c := q[i][0], q[i][1]
d := dist[r][c] + 1
for _, s := range steps {
y, x := r+s[0], c+s[1]
if y < 0 || y >= h ||
x < 0 || x >= w ||
dist[y][x] != -1 {
continue
}
dist[y][x] = d
q = append(q,
[2]int{y, x})
}
}
return dist
}JavaScript の dist[y]?.[x] は、undefined に-1 ではないので
計算量
- 時間は O(m × n) です。
m と n は 行と 列の 数です。 どの マスも 列に 入るのは 1 回だけで、 上下左右の 4 つを 見るだけです - 使う
記憶は O(m × n) です。 答えの 表と列です
よく ある間違い
- 部屋ごとに
出口を探す。 段 1 の 通り O((m × n)²) に なり、 大きな 階で 間に 合いません - 出口を 1 つずつ
始めに して、 小さい方を残す。 出口の 数だけ盤を 探すので、 出口が 多いと 遅くなります。 全部を 同時に 最初の 段にします - 書いた
歩数を、 あとから 小さければ 書き直す。 幅優先では 最初に 書いた 歩数が いちばん 小さいので、 書き直しは 要りません。 書き直す 作りに すると、 同じ マスを 何度も 列に 入れる ことになります - 入力の
1をそのまま 「まだ」の 印に使う。 歩数 1 の マスと 区別できなくなります。 「まだ」には、 歩数に ならない -1 を使います
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「部屋から
出口への 歩数は、 出口から 部屋への 歩数と 同じなので、 全部の 出口を 最初の 段に して 幅優先で 広げます。 答えの 表は 出口を 0、 部屋を -1 で 始め、 取り出した マスの 隣で まだの マスに 歩数 + 1 を 書いて 列に 入れます。 どの マスも 1 回しか 列に 入らないので、 時間は O(m × n) です。」
書き終えたら、
「例 2 では、
両端の 出口を 最初の 段にします。 左の 出口から 2 番目の 部屋に 1、 右の 出口から 4 番目の 部屋に 1 を 書きます。 次に 2 番目の 部屋から まん中に 2 を 書き、 4 番目の 部屋から 見ると まん中は もう 書いてあるので 飛ばします。」