コンテンツにスキップ

第8回:ツリー(1)二分探索木

前回まで:データを並べておけば、二分探索で速く引ける。でも 1 単語追加するたびに並べ直すのは O(n log n)。それを毎回やるのは現実的ではない。

今日の問い: 辞書を作りたい。単語を追加しながら、高速に検索したい。どう置けばいい?

演習8-1

届いた単語順に以下の手順で全部置く:

grape → banana → melon → apple → cherry → lemon → peach

置けたら「cherry」と「fig」を探す。それぞれ何回比較したか記録する。

手順:

  1. 1つ目(grape)は真ん中に書く
  2. 2つ目からは、毎回いちばん上の単語からスタート
  3. 今いる単語と比べ、abc 順で前なら左下・後なら右下へ 1 段おりる
  4. おりた先に単語があれば 3 をくり返す
  5. おりた先が空なら、そこに書く

木構造

演習 8-1 で作ったような構造を「」と呼ぶ。以降の説明では、次の小さな木を例に使う:

graph TD
    5 --> 3
    5 --> 8
    3 --> 1
    3 --> 4
    8 --> 7
    8 --> 9
  • 探す側: 枝をたどるだけ。配列のように全部見る必要がない
  • 追加する側: 空いた枝にぶら下げるだけ。配列の「ずらす」も連結リストの「先頭から全部見る」も起きない

木の言葉

  • 根(root): 一番上の節(例:5
  • 節(node): 値 1 つ分のマス
  • 葉(leaf): 子がいない節(例:1, 4, 7, 9
  • 親 / 子: 線でつながった上が親、下が子

深さ・高さ・部分木

  • 深さ(depth): 根から、その節までの線の本数(root は 0)
  • 高さ(height): 根から一番遠い葉までの線の本数(この木は 2)
  • 部分木(subtree): ある節と、その下にぶら下がる全部
    • 例: 3 の下の 1 / 4

演習8-2

演習 8-1 の木に「kiwi」を追加してみよう。「kiwi」をたどるのに何回比較が必要?


二分探索木

ルール

どの節を見ても:

左の子孫 < 自分 < 右の子孫

これだけ。さっき手で守っていたルールそのもの。

このルールがあるから「前なら左・後なら右」で候補を半分に切れる。

木を読むと、並びが出てくる

ある節では「左の子を全部 → 自分 → 右の子を全部」の順に読む。これを根から、どの部分木でも同じように再帰的に読み上げる(中順走査 / in-order traversal)。

  • 二分探索木は、探すのが速いだけじゃない。整列された並びをいつでも取り出せる
  • だから辞書に向く(速い検索+整った並び、両方)

二分探索 と 二分探索木

二分探索 二分探索木
種類 やり方(アルゴリズム) 入れ物(データ構造)
対象 ソート済み配列 木そのもの
半分に切る判断 真ん中の番号を計算 木の"形" そのもの
追加のコスト 配列=全部ずれて O(n) 枝に掛けるだけで O(log n)

同じ「半分に切る」発想。違いが出るのは追加。だから "木" が要る。

検索と追加

  • 検索:
    • 根から始める
    • 小さければ左、大きければ右。一致で発見。行き止まり(空)なら無い
  • 追加:
    • 同じようにたどって、空いた枝先にぶら下げるだけ(誰もずらさない
    • 同じ単語が来たら何もしない(辞書なので重複させない)

計算量

  • 比較の回数は、たどる線の本数 = 高さで頭打ち
  • バランスのとれた木なら、高さ ≈ \(\log n\)
    • 各段で節の数が倍に増える → n 個収めるのに約 \(\log_2 n\) 段で済む
  • だから検索も追加も O(log n)
  • 検索と追加は同じ道をたどる → コストも同じ

演習8-3

次の順番で届いた単語で木を作ろう。できた形をもとに「peach」を探し、何回比較したか数えよう。

apple → banana → cherry → grape → lemon → melon → peach

演習8-4

演習 8-2 で完成させた木(kiwi を追加した状態)を根から「左の子を全部 → 自分 → 右の子を全部」の順で再帰的に読み上げ、出てきた単語を順に書け。

演習 8-1 / 8-2 のネタバレ注意:木の形を確認したい場合はここを開く
graph TD
    grape --> banana
    grape --> melon
    banana --> apple
    banana --> cherry
    melon --> lemon
    melon --> peach
    lemon --> kiwi

演習8-5

次の 3 つの場面で、ソート済み配列 / 連結リスト / 二分探索木 のどれが最適? 理由も一言。

  • (a) 一度作ったら変わらないデータを、ひたすら検索だけする
  • (b) データが来た順に末尾へ足すだけ。検索はしない
  • (c) 単語を足しながら、何度も検索する

まとめ

  • 木の言葉: 根・節・葉・親子/深さ・高さ・部分木
  • 二分探索木: 左 < 自分 < 右。検索も追加もたどる線 = 高さ分(バランス時 O(log n))。偏ると O(n)
  • 左 → 自分 → 右で読むと整列が出てくる = 整った辞書そのもの
  • 半分にする」が、探索 → 並べ替え → 木 と一貫して効いてきた

次回予告

今日まで:二分探索木なら、検索も追加も速い(バランス時 O(log n))。でも、入れる順が悪いと木が偏り、O(n) に逆戻りすることがある。

「どんな順で入れても、勝手に整って速さを保つ」木があれば……。

次回:ツリー(2)平衡二分探索木。順番に振り回されない木は、どう作る?