幅優先探索 (BFS) と 辞書で つながりの グラフを写し取る
1 つの
海外で定番の問題リスト 4 本には入っていません
問題
社内のadj[i] が i 番の
新しいstart 番の
start番の人を 新しい 0 番にします - そこから
幅優先探索 (BFS) で 人を たどります。 ある 人の 隣を 見る 順は、 adjの並びの 順です。 初めて 見た 人に、 次の 新しい 番号を振ります
写した
- 人の
数は 1 以上 100 以下です。 自分 自身との つながりや、 同じ 2 人の 2 重の つながりは ありません startは 0 以上、人の 数より 小さい整数です
入力と出力の例
例 1
0、
例 2
人が 1 人で、
例 3
0 と 1、
面接での聞かれ方
面接では、
- 「無向グラフの 1 つの
節が 渡されます。 グラフ全体を 深く 写して (ディープコピーして ) 返してください」 - 節は
クラス (値と、 隣の 節の 並び) で 渡され、 写した 節を 返す形が 多いです。 ここでは 頁で 値を 受け渡すため、 節を 番号で 表し、 写した 節に 振る新しい 番号で つながりを 返す形に しています。 どちらも 「元の 節から 写した 節への 対応を 辞書に 持つ」所が同じです - 書き終えると
「輪が あっても 止まりますか?」 「同じ節を 2 回写していませんか?」と 聞かれます
つながりが
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、
関わる学び
グラフ探索は、
まず 考える手順
段 1
隣を
段 2
元の
段 3
列のstart から
段 4
start から
解答
Python
Python
from collections import deque
def clone_graph(adj, start):
ids = {}
out = []
q = deque()
def visit(u):
# 初めて見た人だけを写す
if u not in ids:
ids[u] = len(out)
out.append([])
q.append(u)
return ids[u]
visit(start)
while q:
v = q.popleft()
row = out[ids[v]]
for u in adj[v]:
row.append(visit(u))
return outJavaScript
JavaScript
function cloneGraph(
adj, start,
) {
const ids = new Map();
const out = [];
const q = [];
// 初めて見た人だけを写す
const visit = (u) => {
if (!ids.has(u)) {
ids.set(u, out.length);
out.push([]);
q.push(u);
}
return ids.get(u);
};
visit(start);
for (const v of q) {
const row = out[ids.get(v)];
for (const u of adj[v]) {
row.push(visit(u));
}
}
return out;
}Go
Go
func cloneGraph(
adj [][]int, start int,
) [][]int {
ids := map[int]int{}
out := [][]int{}
q := []int{}
// 初めて見た人だけを写す
visit := func(u int) int {
id, ok := ids[u]
if !ok {
id = len(out)
ids[u] = id
out = append(out, []int{})
q = append(q, u)
}
return id
}
visit(start)
for i := 0; i < len(q); i++ {
v := q[i]
me := ids[v]
for _, u := range adj[v] {
id := visit(u)
out[me] = append(
out[me], id)
}
}
return out
}JavaScript は for...of で、i で、shift でfor...of は、out[me] をvisit のappend で out が
計算量
- 時間は O(V + E) です。
V は 写す人の 数、 E は つながりの数です。 どの 人も 列に 1 回並び、 どの つながりも 両側から 1 回ずつ見ます - 使う
記憶は O(V + E) です。 写した 連絡網 その ものと、 辞書と列です
よく ある間違い
- 写したか
どうかを 覚えない。 輪の ある グラフで 止まらなくなるか、 同じ 人を 何回も写します - 列に
並べる ときではなく、 取り出すときに 辞書に入れる。 列の 中に 同じ 人が 2 回並び、 新しい 番号が 2 つ振られて、 つながりの 番号が ずれます。 番号は 見つけた ときに振ります - 隣の
順を 並べ替える。 新しい 番号の 振り方が 決まりと 変わり、 答えが 1 つに 決まらなくなります。 adjの順のまま見ます - 元の
番号のまま返す。 写したことにならず、 例 1 の 番号が 全部 ずれます。 返す 並びの 中身は、 全部新しい番号です
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「元の
節から 写した 節への 対応を 辞書に 持ちます。 節を 見つけたら、 辞書に 無ければ 写して 新しい 番号を 振り、 列に 並べます。 列から 取り出した 節の 隣を 順に 見て、 写した 節どうしを つなぎます。 辞書が あるので 輪が あっても 止まり、 どの 節も 1 回しか 写しません。 時間は O(V + E) です。」
書き終えたら、
「例 1 で 3 から
始めます。 3 を 新しい 0 に して 列に 入れます。 3 の 隣の 1 と 2 は 初めてなので 新しい 1 と 2、 新しい 0 の 並びは [1, 2] です。 1 を 取り出すと、 隣の 0 は 初めてで 新しい 3、 3 は 写し済みで 0、 並びは [3, 0] です。 2 も [3, 0]、 最後の 0 は [1, 2] です。」