再帰で 二分木の 中に 別の 木と 同じ 部分木が あるか確かめる
大きい
海外で定番の問題リスト 4 本には入っていません
問題
2 本のroot1 と root2 をroot1 のroot2 のtrue、false を
- 部分木は、
ある 節から 下を 葉まで全部含めた物です。 途中で 切った 形は 部分木では ありません root1の木その ものも、 root1の部分木の 1 つです - 各節は
val(値)、left(左の子)、 right(右の子) を 持ちます。 子が 無い ところは、 Python では None、JavaScript では null、Go では nilです。Go の 節は TreeNodeで、Val、Left、Rightと大文字で 始まります root1の節の 数は 1 以上 2000 以下、 root2の節の 数は 1 以上 1000 以下、 値は どれも -10000 以上 10000 以下です
例では、null です。
入力と出力の例
例 1
根が 8、root1 の 6 のroot2 と
例 2
root1 の 6 のroot2 と
例 3
値がroot1 の 2 のroot2 は 2 の
面接での聞かれ方
木の
- 「この
木の 中に、 もう 1 本の 木と 同じ 部分木が あるかを 調べてください」 - 2 本の
木が 同じかを 調べる 問題 (この 一覧の same-tree) の 続きとして 出される ことが よくあります - 書き終えた
あとに 「もっと 速く できますか?」と 聞かれる ことがあります。 木を 区切りの 付いた 文字列に 直して 文字列の 中を 探す 書き方や、 部分木ごとに 指紋 (ハッシュ) を 付ける 書き方で、 O(m + n) にできます
ジュニアの
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「再帰と木」に、
関わる学び
再帰は、
まず 考える手順
段 1
まず、same(a, b) を
段 2
root1 のroot1 のroot1 のsame(その節, root2) を
段 3
節をsame がtrue、true です。false です。
段 4
例 2 のようにsame は
解答
Python
Python
def same(a, b):
if a is None or b is None:
return a is b
if a.val != b.val:
return False
return (same(a.left, b.left)
and same(a.right,
b.right))
def subtree_check(root1, root2):
if root1 is None:
return False
if same(root1, root2):
return True
return (subtree_check(
root1.left, root2)
or subtree_check(
root1.right, root2))JavaScript
JavaScript
function same(a, b) {
if (a === null
|| b === null) {
return a === b;
}
return a.val === b.val
&& same(a.left, b.left)
&& same(a.right, b.right);
}
function subtreeCheck(
root1, root2
) {
if (root1 === null) {
return false;
}
if (same(root1, root2)) {
return true;
}
return subtreeCheck(
root1.left, root2
) || subtreeCheck(
root1.right, root2
);
}Go
Go
func same(a, b *TreeNode) bool {
if a == nil || b == nil {
return a == b
}
return a.Val == b.Val &&
same(a.Left, b.Left) &&
same(a.Right, b.Right)
}
func subtreeCheck(
root1, root2 *TreeNode,
) bool {
if root1 == nil {
return false
}
if same(root1, root2) {
return true
}
return subtreeCheck(
root1.Left, root2,
) || subtreeCheck(
root1.Right, root2,
)
}same のtrue、false を 1 行で
計算量
- 時間は O(m × n) です。
m は root1の節の 数、 n は root2の節の 数で、 root1の節ごとに sameが多くて n 個の 節を比べます - 使う
記憶は O(h) です。 h は root1の高さで、 再帰の 呼び出しが 深さの 分だけ積まれます。 1 本道の 木では O(m) になります
よく ある間違い
- 根の
値が 同じ節だけを 試し、 見つかったら終える。 値が 同じ節は 何個も ある ことがあります。 1 つ目で sameが成り立たなくても、 ほかの 節で 成り 立つことがあるので、 全部の 節を試します - 途中まで
同じなら 同じと見る。 root2の葉まで 比べて 終えると、 例 2 の 余分な 5 を 見落とします。 sameは両方の 空まで比べます - 値の
並びを 文字列に して探す。 空の 子の 印や 区切りを 付けずに 文字列に すると、 12 と 2 が 「2」で 当たったり、 形の 違う 木が 同じ 文字列に なったりします。 文字列で 探すなら、 値の 前後に 区切りを 付け、 空の 子も 印で書きます - 左と
右の どちらかで 見つかったのに、 もう 片方の 結果で 上書きする。 or(||) で つなぎ、 どちらかで 見つかれば trueにします
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「2 本の
木が 同じかを 調べる 関数を 先に 作ります。 両方空なら 同じ、 片方だけ 空か 値が 違えば 違う、 あとは 左どうしと 右どうしを 比べます。 そのうえで、 root1 の 節を 1 つずつ根に 見立てて 同じかを 試し、 どこかで 成り 立てば true です。 時間は O(m × n) です。」
書き終えたら、
「例 1 では、
まず根の 8 と root2 を 比べ、 8 と 6 で 違います。 左の 子の 6 を 根に 見立てると、 6 と 6、 2 と 2、 7 と 7 が 同じで、 その 下も 全部空どうしなので true です。」