データ構造とアルゴリズム
第七回
(2012年11月26日)
辞書とその実装: 二分木など
http://www.sw.it.aoyama.ac.jp/2012/DA/lecture7.html
Martin J. Dürst
© 2008-12 Martin
J. Dürst 青山学院大学
目次
- 前回の残り・まとめ
- 辞書
- 二分木とその辿り方
- 二分探索木
- 平衡木
前回の残り
- O(n log n) より早い整列法
- C や Ruby での整列
前回のまとめ
- クイックソートは効率のよい整列法
- 最悪の場合に O(n2); 平均で
O(n log n)
- 分割要素の選択など実装で注意点が多い
- 実装例: 6qsort.rb
- 様々な整列法のアニメーション: sort.svg
辞書の抽象データ型
(dictionary; 注: 実際の辞書とは違う)
- データ項目ごと
- キー (key): 項目の特定のため、探索に使用
- 値 (value): キー以外の項目ごとの情報、無しも可
- 操作
- 挿入 (insert)
- 削除 (delete)
- 探索 (search/find)
辞書の簡単な実装
- 整列済の配列: 探索は二分探索で O(log
n)、挿入・削除は O(n)
- 未整列の配列、連結リスト: 探索は O(n)
- 挿入・削除・探索を全て O(log
n) 以内で実装したい
二分木
- グラフ (graph): 頂点 (ノード、node) と辺 (edge)
からなる
- 木 (tree): 根 (root) は親 (parent)
が無いが、他の頂点が全て一つの親とつながっている
- 二分木 (binary tree): 各頂点に子 (child) が最大 2 個
二分木の辿り方
- 深さ優先 (depth first)
- 行きがけ順 (preorder)
- 通りがけ順 (inorder)
- 帰りがけ順 (postorder)
- 幅優先 (breadth first)
二分探索木
- 各頂点にデータ項目を配置
- 任意の頂点のキーが k の場合
- 左の部分木のキーが k より小さい
- 右の部分木のキーが k より大きい
- 同等なキーが複数ある場合の扱いは実装依存
探索木での探索
- 根から探索を開始
- 現在の頂点のキーより探索のキーが
- 同じの場合、頂点のデータ項目を返す
- 小さい場合、左の部分木を探索
- 大きい場合、右の部分木を探索
- 空の頂点の場合、探索終了
探索木での挿入
- 根から挿入を開始
- 現在の頂点のキーより挿入のキーが
- 小さい場合、左の部分木に挿入
- 大きい場合、右の部分木に挿入
- 空の頂点の場合、挿入の頂点に置換
(その子は空の頂点)
- 同じ場合、挿入打ち切り、右の部分木に挿入、など
探索木での削除
- 削除したい頂点を発見 (探索参照)
- 削除したい頂点に (真の) 子が
- ない場合にそのまま削除
- 一個だけの場合、それと置換
- 二個の場合、右の部分木の一番小さいキーの頂点と置換
探索木の実装
- 子が無い場合、特殊なノード (
NilNode
)
を全てのノードで共有
- Ruby での実装: 7bintree.rb
単純な探索木の評価
- 木全体の高さで操作の計算時間が決まる
- 最善の高さが O(log
n)
- 最悪の高さが O(n)
- 平均の高さが O(log
n)
平衡木
(balanced tree)
- 一般の探索木は最悪、連結リストの形を取る
- 挿入・削除の順番が決まっているため、
(クイックソートのように)
乱数などで分割項目を決めることは不可能
- 完全二分木の場合、追加・削除の手間が課題
解決策: 完全ではないがある程度平衡性を保つ木
トップダウン 2-3-4 木
(top-down 2-3-4 tree)
- 各ノードの子数は 2、3 または 4
- 子数が k の場合、ノードに k-1
のキーとデータ項目を保持
- ノード内のデータ項目のキーは部分木の分岐点
- 木の高さは一定
- 一番下の層には子がない
(実装上、全部同一の空のノード)
次回への宿題
(提出無し)
- データ項目が n
の場合の最低と最大の木の深さを考える
- 2-3-4
木の追加の操作を複数の例を使って考えて、アルゴリズムを提案