データ構造とアルゴリズム
第七回
(2010年11月25日)
辞書とその実装: 二分木など
http://www.sw.it.aoyama.ac.jp/2011/DA/lecture7.html
Martin J. Dürst
© 2008-11 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)
平衡木
- 一般の探索木は最悪、連結リストの形を取る
- (クイックソートのように)
乱数などで分割項目を決めることは不可能
- 完全二分木の場合、追加・削除の手間が課題
解決策: 完全ではないがある程度平衡性を保つ木
トップダウン 2-3-4 木
(top-down 2-3-4 tree)
- 各ノードの子数は 2、3 または 4
- 子数が k の場合、ノードに k-1
のキーとデータ項目を保持
- ノード内のデータ項目のキーは部分木の分岐点
- 木の高さは一定
- 一番下の層には子がない
(実装上、全部同一の空のノード)
次回への宿題
(提出無し)
- データ項目が n
の場合の最低と最大の木の深さを考える
- 2-3-4
木の追加の操作を複数の例を使って考えて、アルゴリズムを提案