1. 空木 -> true
  2. [10] 単一ノード -> true
  3. [10,5,15] 正しい BST -> true
  4. [10,5,15,3,7,12,20] 正しい BST -> true
  5. [10,5,15,null,null,6,20] 6 が右部分木にあり不正 -> false
  6. [5,10,15] 左の子が親より大きい -> false
  7. [10,5,5] 重複 -> false
コース一覧
コンピューターサイエンス上級:アルゴリズムとデータ構造
BST 妥当性チェック

コンピューターサイエンス上級:アルゴリズムとデータ構造

探索、ソート、木、グラフ、動的計画法、貪欲法など、競技プログラミングや技術面接で問われる高度なアルゴリズムとデータ構造を学べるコースです。基本的な CS の知識を持ち、アルゴリズム力を伸ばしたい学習者や、外資・大手の技術面接対策をしたい方を対象としています。約 13 時間 (1 日 30 分 × 26 日) で 50 レッスンを修了でき、修了後はコーディング面接の実装問題を構造的に解けるようになります。

1
連結リスト
01. リンクリスト構築と長さの計算5分
02. リンクリストの反転5分
03. リンクリストのサイクル検出5分
04. ソート済みリンクリストの merge5分
05. リンクリストの中央ノード取得5分
06. ソート済みリストの重複削除5分
07. 第1章まとめクイズ5分
2
二分木
01. 二分木の in-order 走査5分
02. 二分木の pre-order 走査5分
03. 二分木の post-order 走査5分
04. 二分木の幅優先走査 (BFS)5分
05. 二分木の高さ5分
06. 二分木の平衡判定5分
07. 第2章まとめクイズ — 二分木5分
3
探索木 (BST)
01. BST に値を挿入する5分
02. BST から値を検索する5分
03. BST の最小値と最大値5分
04. BST 妥当性チェック5分
05. BST で k 番目に小さい値5分
06. BST から値を削除する5分
07. 第 3 章 まとめクイズ5分
4
ハッシュとセット
01. hashmap で頻度集計5分
02. キーでグループ化5分
03. two sum (hash で O(n))5分
04. 部分配列の和 = k の個数5分
05. 最長連続部分列5分
06. 集合の積 (intersection)5分
07. 第4章まとめクイズ5分
5
グラフ
01. グラフ BFS で連結成分サイズを求める5分
02. グラフ DFS で連結成分の数を数える5分
03. グラフのパス存在判定5分
04. BFS で最短経路の長さを求める5分
05. トポロジカルソート5分
06. 2 部グラフ判定5分
07. ダイクストラ法 — 重み付きグラフの最短経路15分
08. 第5章まとめクイズ — グラフ5分
6
動的計画法 (上級)
01. 編集距離 (レーベンシュタイン距離)5分
02. 最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法5分
03. 最大部分配列和 (Kadane)5分
04. 隣り合わない最大値 (House Robber)5分
05. グリッド経路数 (Unique Paths)5分
06. 単語分割可能か (Word Break)5分
07. 第6章まとめクイズ — 動的計画法 (上級)5分
7
総合データ構造と関数型
01. map と filter を組み合わせる5分
02. reduce で積を計算5分
03. 関数合成5分
04. カリー化5分
05. trie の単純検索 (prefix マッチ)5分
06. Union-Find (連結成分数)5分
07. 最終総まとめクイズ5分

BST 妥当性チェック

親と子だけを比べる実装は、この木を通してしまう

判定を書けと言われて、まず思いつくのは次の形です。

Python

# よくある間違い — 自分の子としか比べていない def looksOk(node): if node is None: return True if node.left and node.left.val >= node.val: return False if node.right and node.right.val <= node.val: return False return looksOk(node.left) and looksOk(node.right)

各ノードで自分の子を 2 つ見て、駄目なら偽。木全体も再帰で回っているので、抜けは無さそうに見えます。ところが次の木を渡すと、この関数は真を返します。

diagram (will load when visible)

15 から見れば 6 は左にいて小さいので、親子の比較は通ります。10 から見ても、子は 5 と 15 で問題ありません。どのノードも、自分の子とは正しい関係にあります。それでも、この木は探索木ではありません。小さい順に取り出すと 5, 10, 6, 15, 20 となって、昇順になっていないからです。

6 は誰に対して違反しているのか

6 は 10 の右側にぶら下がっています。右へ降りた以上、6 は 10 より大きくなければいけません。ところが 6 は 10 より小さい。違反の相手は親ではなく、2 つ上の祖先でした。

親だけを見る実装が見逃すのは、まさにここです。ノードが縛られているのは親からだけではなく、通ってきた祖先すべてからです。

降りるたびに、許される幅が狭くなる

そこで、各ノードに「入ってよい値の幅」を持たせて降ります。根には上も下も制限がありません。左へ降りるときは上限が親の値になり、右へ降りるときは下限が親の値になります。上限と下限は、通った祖先の数だけ入れ替わり立ち替わり更新されていきます。

ノード許される幅判定
10 (根)制限なしOK
5 (10 の左)10 未満OK
15 (10 の右)10 より大きいOK
6 (15 の左)10 より大きく 15 未満違反

6 の行だけ、上と下の両方から挟まれています。この幅を子へ引き継いでいけば、祖先との関係も自動で見られるわけです。制限が無い状態は、次のように書けます。

Python

LOW = float('-inf') HIGH = float('inf')

JavaScript なら -Infinity と Infinity です。整数の最小値と最大値を自分で決め打ちすると、その値がデータに入っていたときに判定が崩れます。

等しい値と、空の木

同じ値が 2 つある木は、今回は不正とします。幅の判定を「以上」「以下」で書くと重複を通してしまうので、上下とも等号を含めない形にしてください。

ノードが 1 つも無い木は、破っている約束が 1 つも無いので正しい木です。真を返します。ここを偽にすると、子のいない枝へ降りた瞬間に全体が偽になり、どんな木も通らなくなります。

要件

  1. 各ノードに対し、許される値域 [lo, hi] を引き継いで判定する
  2. 厳密不等式 lo < val < hi で重複を不正と判定する
  3. 空木は BST とみなして true を返す

入出力例

isBst([]) → true isBst([10]) → true isBst([10,5,15]) → true isBst([10,5,15,3,7,12,20]) → true isBst([10,5,15,null,null,6,20]) → false isBst([5,10,15]) → false isBst([10,5,5]) → false

ヒント

「直接の子だけを親と比較」だと不十分。部分木全体の値域チェックが必要

再帰関数に `lo, hi` を渡し、左の子に進むときは `hi=node.val`、右の子に進むときは `lo=node.val` に更新

別解として in-order 走査して昇順になっているかチェックでも OK

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

関連レッスン

  • BST で k 番目に小さい値

    BST の in-order 走査が昇順になる性質を使い、k 番目に小さい値を返す関数を実装する。

  • BST から値を削除する

    BST から指定の値を削除し、削除後の in-order 走査結果を配列で返す関数を実装する。

  • 第 3 章 まとめクイズ

    探索木 (BST) の挿入・検索・削除・妥当性チェックなど、第 3 章の要点を確認する 4 択クイズ。

  • hashmap で頻度集計

    配列の各要素が何回現れたかを `hashmap` で集計し、最も多く出た値を返す関数を実装する。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 再帰関数が自分自身を呼び出す処理パターン
  • 関数処理に名前を付けて再利用できる単位
  • JavaScriptWeb で動的な振る舞いを担う言語
main.py
学習モード
エディタを読み込んでいます

メモ

BST 妥当性チェック

⌘S で保存