コンピュータアルゴリズム 2 6. コンテナの実現

67
ココココココココココココ 2 6. コココココココ 田田 w.logos.ic.i.u-tokyo.ac.jp/~tau/lecture/software/

Upload: jasmine-khorvash

Post on 03-Jan-2016

46 views

Category:

Documents


2 download

DESCRIPTION

コンピュータアルゴリズム 2 6. コンテナの実現. 田浦. http://www.logos.ic.i.u-tokyo.ac.jp /~tau/lecture/software/. Part III : コンテナの実現. 実現方法を語る上での目標と仮定. 目標 : 各操作の計算量をコンテナ中の要素数や操作の回数の関数として見積もる それをできるだけ小さくする 「万能」コンテナ  すべての操作を一定時間で行える  は存在しない ( 存在すればコンテナは一種類でよかった !) 仮定 ( 使える道具 ) 配列 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: コンピュータアルゴリズム 2 6.  コンテナの実現

コンピュータアルゴリズム 2

6. コンテナの実現

田浦

http://www.logos.ic.i.u-tokyo.ac.jp/~tau/lecture/software/

Page 2: コンピュータアルゴリズム 2 6.  コンテナの実現

Part III : コンテナの実現

Page 3: コンピュータアルゴリズム 2 6.  コンテナの実現

実現方法を語る上での目標と仮定 目標 :

各操作の計算量をコンテナ中の要素数や操作の回数の関数として見積もる

それをできるだけ小さくする 「万能」コンテナ すべての操作を一定時間で行える は存在

しない ( 存在すればコンテナは一種類でよかった !) 仮定 ( 使える道具 )

配列 固定の要素を持ったレコード ( 以降オブジェクト )

C : struct, C++ : struct, class, Java : class 配列,オブジェクトの動的な生成 ( メモリ確保 ) が定数時間で

行える C : malloc, C++, Java : new

配列,オブジェクトの要素の参照は定数時間で行える

Page 4: コンピュータアルゴリズム 2 6.  コンテナの実現

頭の使い方:データ構造 同じ状態 ( 同じ数列や集合 ) を表すのにも,計算機内

( メモリ内 ) での「表現方法,配置方法」は色々ある 要するに言われたこと ( 挿入,削除,参照 ) が正しくで

きれば文句はない この表現方法や配置方法のことを「データ構造」とい

う データ構造の作り方で各操作の得手・不得手が決まる

たとえば普通の配列も一種のデータ構造 配列は指定された要素の参照が得意だが,新しい要

素の追加は苦手

Page 5: コンピュータアルゴリズム 2 6.  コンテナの実現

データ構造を図示する際の約束(1)

配列 ( 四角を横に並べる )

オブジェクト ( 長方形を縦に並べる )

配列やオブジェクトの要素が他のオブジェクトや配列を参照している (C/C++ のポインタ型 , Java の参照型,配列型 ) 場合,それを矢印で表す

n:

単なる慣習:どちらも実際にはメモリ上の連続したアドレスを占めているp:

q:r:

n:p:q:r:

n:p:q:r:

C/C++:struct A { int n; int * p; A * q; int r; };

Java:class A { int n; int[] p; A q; int r; };

Page 6: コンピュータアルゴリズム 2 6.  コンテナの実現

データ構造を図示する際の約束(2)

特に null ポインタ (C/C++ の 0, または NULL, Java のnull) は,矢印を書かないか,以下のように明示する

n:

q:r:

p:

実際には p に null (0) が代入されている状態

Page 7: コンピュータアルゴリズム 2 6.  コンテナの実現

列 理想的には以下をすべて効率的に行いたい

i 番目となる要素の追加挿入 i 番目の要素の削除 i 番目の要素の参照,置き換え

Page 8: コンピュータアルゴリズム 2 6.  コンテナの実現

配列を基にした列 ( 可変長配列 )

Java の ArrayList, C++ の vector 常に下図のような状態を保つ

a は capacity 個の要素を格納できる配列 a[0] ... a[n-1] に要素が入っている.残りは「空き部

屋」ncapacitya

capacity 個

n 個(C/C++)typedef struct ArrayListX { int capacity; int n; X * a;} ArrayListX;

Page 9: コンピュータアルゴリズム 2 6.  コンテナの実現

可変長配列の操作の実際 i 番目の要素参照,置き換え : 自明 i 番目となる要素の挿入

配列が満杯だったらより大きな配列を取り直し,中身を新配列へコピー

もともと i 番目およびそれ以降の要素をひとつずつ後ろへずらす

i 番目の要素の削除 もともと i + 1 番目およびそれ以降の要素をひとつ

ずつ前へずらす

Page 10: コンピュータアルゴリズム 2 6.  コンテナの実現

速い・遅い操作は ?

i 番目となる要素の追加 O( ) 特に,末尾への追加 O( ) 特に,先頭への追加 O( )

i 番目の要素の削除 O( ) 特に,末尾からの削除 O( ) 特に,先頭からの削除 O( )

i 番目の要素の参照・置き換え O( )

Page 11: コンピュータアルゴリズム 2 6.  コンテナの実現

配列があふれたらどのくらい大きくすべきか ?

capacity = capacity + 1 ? capacity = capacity + 10 ? capacity = capacity * 2 ?

もちろん ( 未来に何が行われるかわからない限り ) どちらがいいと断定はできないが,「 + 一定値」ではなく「 * 一定値」とすることには重要な意味がある

Page 12: コンピュータアルゴリズム 2 6.  コンテナの実現

配列伸張方式による差 連続して n 回の「末尾への追加」が行われた場合の「合

計」の計算量を考える ここでは必要な要素のコピー回数で計算量を見積もる 配列の最初の大きさは 1 とする

+ 1 の場合 (+10, +100, ... でも結果は同じ ) 1 + 2 + 3 + 4 + ... + (n – 1) O(n2)

* 2 の場合 (*1.5, *1.2, ... でも結果は同じ ) 1 + 2 + 4 + 8 + ... + 2m 2m+1 – 1 O(n)

つまり平均すれば O(1) 償却計算量 (amortized complexity) O(1) ともいう

n

Page 13: コンピュータアルゴリズム 2 6.  コンテナの実現

速い・遅い操作再掲 i 番目となる要素の追加 O( )

特に,末尾への追加 O( ) (amortized O(1)) 特に,先頭への追加 O( )

i 番目の要素の削除 O( ) 特に,末尾からの削除 O( ) 特に,先頭からの削除 O( )

i 番目の要素の参照・置き換え O( )

Page 14: コンピュータアルゴリズム 2 6.  コンテナの実現

リンクリストを基にした列 実例 : Java LinkedList, C++ list, Python リスト

追加は必ず新しい要素を入れる「箱」を新しく割り当てて行う

head

tail

(C/C++)typedef struct Cell { struct Cell * next; struct Cell * prev; X value;} Cell;

typedef struct LinkedListX { Cell * head; Cell * tail;} LinkedListX;

Page 15: コンピュータアルゴリズム 2 6.  コンテナの実現

挿入操作の実際 (Java)

void add_to_front(X x) { Cell c = new Cell(); c.x = x; c.prev = null; c.next = head; if (head == null) tail = c; else head.prev = c; head = c;}

head

tail

x

Page 16: コンピュータアルゴリズム 2 6.  コンテナの実現

挿入操作の実際 (C)

void add_to_front(LinkedListX * l, X x) { Cell * c = (Cell *) malloc(sizeof(Cell)); c->x = x; c->prev = null; c->next = head; if (l->head == null) l->tail = c; else l->head->prev = c; l->head = c;}

head

tail

x

Page 17: コンピュータアルゴリズム 2 6.  コンテナの実現

速い・遅い操作は ?

i 番目となる要素の追加 O( ) 特に,末尾への追加 O( ) 特に,先頭への追加 O( )

i 番目の要素の削除 O( ) 特に,末尾からの削除 O( ) 特に,先頭からの削除 O( )

i 番目の要素の参照・置き換え O( ) 注 : 0, 1, ..., n – 1 番目の要素を順に参照する O( )

cf. Java, C++ の iterator

Page 18: コンピュータアルゴリズム 2 6.  コンテナの実現

練習課題 (1)

配列を用いた可変長配列 (Java の ArrayList, C++ の vector に相当するデータ構造 )

リンクリストを用いた可変長配列 (Java の LinkedList, C++ の listに相当するデータ構造 )

を自分で作ってみよ (C, C++, または Java で ) 先頭・末尾への挿入・削除 任意の値の参照

挿入するデータの型は決めてよい ( 文字列など ) 以下の性能測定をして gnuplot などでグラフ表示せよ

N 個のデータを末尾に挿入する N 個のデータを末尾に挿入後,それらを先頭から取り出す 多数のデータ挿入後に,ランダムな要素の参照を N 回

Page 19: コンピュータアルゴリズム 2 6.  コンテナの実現

クイズ (1)

ファイルを全行読んで,反対向きに表示するプログラムを書くとする.やってはいけないのは ? 案 1: LinkedList を用いて,行を先頭に追加 していく.全部追加後,前から print

案 2: ArrayList を用いて同様 案 3: LinkedList を用いて,行を末尾に追加 .全部追

加後,後ろから print 案 4: ArrayList を用いて同様

Page 20: コンピュータアルゴリズム 2 6.  コンテナの実現

擬似コード 案 1, 2

{Linked,Array}List<String> L = new {Linked,Array}List<String>();for (ファイル中の各 line) L.add(0, line);for (line : L) print line;

案 3, 4{Linked,Array}List<String> L = new {Linked,Array}List<String>();for (ファイル中の各 line) L.add(line);for (i = 0; i < 行数 ; i++) print L.get( 行数 – i – 1);

Page 21: コンピュータアルゴリズム 2 6.  コンテナの実現

クイズ (2)

i 番目の要素の参照 O(1) 先頭・末尾への追加 amortized O(1) 先頭・末尾からの削除 O(1)

を達成する方法は ? C++ deque がそれをやっている模様

Page 22: コンピュータアルゴリズム 2 6.  コンテナの実現

練習課題 (2)

配列用のクイックソートをそのまま LinkedList に置き換えると計算量はどうなるか (a[i] を単純に get(a, i) に置き換える )?

LinkedList用の O(n log n) クイックソートを書け アルゴリズム自体の考え方は同じ コーディングを気をつけなくてはいけないだけ

Page 23: コンピュータアルゴリズム 2 6.  コンテナの実現

写像,連想記憶 Java : XXXMap, STL: map, Python : 辞書 以下をすべて効率的に行いたい

対応 key value の追加 Java : m.put(key, value) Python/STL : m[key] = value

key に対する対応 key X の削除 Java : m.remove(key) Python/STL : del m[key]

key に対応する value の探索 Java : value = m.get(key) Python/STL : value = m[key]

Page 24: コンピュータアルゴリズム 2 6.  コンテナの実現

key に関する前提 key は以下を満たす任意の値

key 同士の等号比較ができる (当然の条件 ) 時に, key 間に全順序関係が存在すること,それに

基づく不等号比較ができることを仮定する 挿入されたあと key 自身が変化 ( 上記の等号の意味に

おいて ) することはない たとえば key 自身が配列で,挿入された後に変更

されることはない

Page 25: コンピュータアルゴリズム 2 6.  コンテナの実現

連想記憶の実現法 使われているデータ構造による分類

配列や列 ( リストなど ) 木構造 ハッシュ表

Page 26: コンピュータアルゴリズム 2 6.  コンテナの実現

配列を用いた連想記憶 連想記憶 = key の列と value の列

Java: class map { ArrayList<K> a; ArrayList<V> b;}; 二通りのやり方 :

その 1: 組 k v の挿入は末尾に行う (amortized O(1))

put(k, v) { a.add(k); b.add(v); }探索は線形探索 O(n)

その 2: つねに整列 (a0 < a1 < a2 < ... ) しておく探索は 2分探索 O(log n) 挿入は ai k < ai+1 なる i を見つけて挿入 O(n)

キーの列 値の列

Page 27: コンピュータアルゴリズム 2 6.  コンテナの実現

2分探索 入力 :

整列された配列 a[0], ..., a[n – 1] i 0 i < n–1 a[i] < a[i+1]

見つけたい key k 出力 ( 以下の i):

下にある例外ケースを除き, a[i] k < a[i+1] を満たす i (0 i < n – 1)

いくつかの例外ケース n = 0 ならば i = –1 n > 0 かつ k < a[0] ならば i = –1 n > 0 かつ a[n – 1] k ならば i = n – 1

Page 28: コンピュータアルゴリズム 2 6.  コンテナの実現

2分探索の基本アイデア 求める i が存在する可能性のある範囲 を狭めていく

(挟みうち ) a[p] k < a[q] を保ちながらループ ( 不変条件 )

2 4 5 7 8 10 12 13 90 98

p q

Page 29: コンピュータアルゴリズム 2 6.  コンテナの実現

2分探索コード

int find_idx(k) {n = a.size();if (n == 0) return –1;if (k < a[0]) return –1;if (a[n–1] k) return n–1;// a[0] k < a[n–1], n > 0int p = 0; int q = n–1;while (q – p 2) { // a[p] k < a[q], q – p 1 int c = (p + q) / 2; if (a[c] k) p = c; else q = c;}// a[p] k < a[q], 1 q – p < 2// a[p] k < a[p+1]return p;

}

Page 30: コンピュータアルゴリズム 2 6.  コンテナの実現

探索・挿入・削除 get(k) {

i = find_idx(k); if (i 0 && a[i] == k) return b[i]; else not_found;}

put(k, v) { i = find_idx(k); if (i 0 && a[i] == k) { a.replace(i, k); b.replace(i, v); } else { a.add(i+1, k); b.add(i+1, v); }}

remove(k) { i = find_idx(k); if (i 0 && a[i] == k) { a.remove(i); b.remove(i); } else { error /* no such key */ }}

Page 31: コンピュータアルゴリズム 2 6.  コンテナの実現

練習問題 ホーア論理を用いて find_idx の正しさを証明せよ

特に,ループ不変条件を見出せ 計算量 O(log n) を示せ

{ ????? }while (q – p 2) { { ????? } int c = (p + q) / 2; if (a[c] k) p = c; else q = c;}{ ????? }

Page 32: コンピュータアルゴリズム 2 6.  コンテナの実現

配列を用いた探索の計算量

線形探索 2分探索検索 O( ) O( )

挿入 amortized O( )

O( )

削除 O( ) O( )

どちらにしてもこの方法での連想記憶はそれほど有用ではない

Page 33: コンピュータアルゴリズム 2 6.  コンテナの実現

木構造を用いた連想記憶

... ...

...

...

木構造 (tree)

リスト (linked list)

配列 (array)

...

...

Page 34: コンピュータアルゴリズム 2 6.  コンテナの実現

各データ構造の特徴 配列

すべてのデータに「一撃で」 (O(1) で ) アクセスできる 挿入・削除が面倒 要素 アドレスの「単純な」対応が原因

リスト 途中データのアクセスに「ポインタの追跡」時間がかか

る (O(n)) ( 先頭末尾への ) 挿入・削除が容易・効率的 「データのおき場所 ( アドレス ) は自由,それをポインタ

でつなぐ」方式に起因 木構造

両者の「中間」

Page 35: コンピュータアルゴリズム 2 6.  コンテナの実現

木構造の基本アイデア 木構造の子供の数が適度 (2 以上,ある一定値以下 ) な

らば, データにたどり着くまでのポインタの追跡 O(log n) 挿入・削除時のデータの書き換え O(log n) とできるのではないか ???

Page 36: コンピュータアルゴリズム 2 6.  コンテナの実現

2分木を用いた連想記憶 2分木 (binary tree) : 子供の数が 0, 1, 2 のいずれかの木 各ノードに key value を格納

... ...

...

“learned” 3

“today” 1

“searching” 5

“I” 2

“about” 4

...

Java:class Node { Key k; Value v; Node left; Node right;};

C++:class Node { Key k; Value v; Node * left; Node * right;};

Page 37: コンピュータアルゴリズム 2 6.  コンテナの実現

効率的な探索のための 2分木の構成

k

< k > k

2分探索木 (binary search tree)

2分探索 (binary search) とは似て非なる言葉

Page 38: コンピュータアルゴリズム 2 6.  コンテナの実現

木構造の亜種 リーフ (葉 ) 以外の子供に (key, value) を格納するか否か

否の場合,途中ノードは, k (閾値 ) だけを格納する m分木

子供の数が 0, 1, …, m – 1 ノードひとつに複数 (> 1) 個の (key, value) を格納

以下の話はこれらの選択によらず共通

k

k > k

< k1

k1, k2

k2k1 x < k2

3分木

Page 39: コンピュータアルゴリズム 2 6.  コンテナの実現

正しい 2分探索木の状態 10, 20, 30, 40, 50, 60 を格納している正しい 2分探索木の

状態例 30

10

20

50

40 60

10

50

60

20

40

30

key の集合が同じでも複数の表現方法 ( 内部状態 ) があることに注意

Page 40: コンピュータアルゴリズム 2 6.  コンテナの実現

2分木中でのデータの並び方 7

3 11

5 1 13 9

0 2 4 6 8 10 12 14

左の子すべて ;自分 ;右の子すべて ;

が「小さい順」

list_elems() { if (left != null) left.list_elems(); print key; if (right != null) right.list_elems();}

これですべての key が小さい順に print される

Page 41: コンピュータアルゴリズム 2 6.  コンテナの実現

2分木に対する操作 : 探索

k

< k > k

あるノード (k) を基点とする key の探索 key = k 見つかった key < k

左の子がいない (null) not found左の子がいる 左の子で再帰呼び出し

key > k 右の子で同様

練習 : 1. コードにしてみよ2. 再帰呼び出しを使わずにループで書いてみよ

Page 42: コンピュータアルゴリズム 2 6.  コンテナの実現

2分木に対する操作 : 挿入 あるノード (k) を基点とする key : value の組の挿入

key = k そのノードに挿入 ( 上書き ) key < k

左の子がいない 左の子を新ノードとして作る左の子がいる 左の子で再帰呼び出し

key > k 右の子で同様

k

< k > k

k k

key

Page 43: コンピュータアルゴリズム 2 6.  コンテナの実現

クイズ : 2分木は本当に O(log n)か ?

つまり,探索,挿入ともデータ数 n に対して O(log n) 時間か ? amortized O(log n) (i.e., n 個挿入するのに O(n log n)

か ? n 個のデータを入れるのに最悪のケースは ?

Page 44: コンピュータアルゴリズム 2 6.  コンテナの実現

実は O(n) 遅いだけでなく,再帰呼び出しは危険 ( スタックオーバーフロー ) ループに書き換える必要がある (練習 )

ただし,「不運な場合」は少ない クイックソートの不運な場合と類似

Page 45: コンピュータアルゴリズム 2 6.  コンテナの実現

クイズ ( 数学 )

n 要素のクイックソート , 空の 2分木への n 要素の挿入の「平均」計算量は O(n log n) か ? 平均計算量

現れ得るデータがみな等確率で現れるとした時の平均

簡単のため全データが異なるとする クイックソートや 2 文木の計算量は要素間の大小関係だけで決まるから , n 要素の順序関係 n! 通りが等確率で現れると仮定してよい

n 要素の配列を作ったとき不運なデータができる確率は無視できるほど小さい (n のとき 0) か ?

Page 46: コンピュータアルゴリズム 2 6.  コンテナの実現

2分木に対する操作 : 削除 key の削除

削除すべきノード (n) を特定するところまでは探索と同じ

主な問題は n を削除した後の木構造の維持 「データ構造の不変条件」の回復

< k > k

n

Page 47: コンピュータアルゴリズム 2 6.  コンテナの実現

削除 (1)

左 (右 ) の子がいない場合 注 : n はルートではない (親がいる ) と仮定している

> k

> k

n n

p p

... ...

Page 48: コンピュータアルゴリズム 2 6.  コンテナの実現

削除 (2)

両方の子がいる場合 ( 再び n は根でないことを仮定 ) p の子を誰にするのが手っ取り早いか ?

< k > k

n

p

...

Page 49: コンピュータアルゴリズム 2 6.  コンテナの実現

注 : nが根だったら ?

問題 : 削除の結果,木の根が入れ替わったら,どこのポインタを書き換えればいいのか ? そのようなポインタは木の外から生えていて,どこ

を書き換えたらいいのか一般にはわからない 解決策 :

木のノードを直接さすことはしないJava:class tree { node root;}

C++:class tree { node * root;}

Page 50: コンピュータアルゴリズム 2 6.  コンテナの実現

クイズ 整列に 2分木が使える

Page 51: コンピュータアルゴリズム 2 6.  コンテナの実現

平衡木 挿入・削除・探索が O(log n) でできることを保証するには ?

子供の大きさが極端に偏らなけれ ( ある「平衡条件」を満たせ ) ば良い

挿入・削除の結果「平衡条件」がくずれたら直す いくつかの種類

AVL木 (左右の子の高さの差が 0 または 1 以内 ) Red-Black木 B木

根・葉以外の頂点の子供の数が m/2 以上 m 以下 例 : 2 以上 3 以下 (2-3木 ) , 3 以上 5 以下 葉の子供の数はもちろん 0

葉までの深さはどこも同じ 教科書 (石畑や Cormen et al.) で自習してください

Page 52: コンピュータアルゴリズム 2 6.  コンテナの実現

ハッシュ表を用いた連想記憶 実はシンプルかつ,実践的にも速い,最も popular な方

法 4学期の授業でやった ( んですよね ?) 教科書 (石畑本 ) で復習して下さい

Page 53: コンピュータアルゴリズム 2 6.  コンテナの実現

基本は各要素が key, value のリストになった配列

あるキーがどのリストに入るかの決め方 : 「ハッシュ関数」という適当な , 0, …, H – 1 (H は配列の要素数 ) の値をとる関数で決める

ハッシュ表のデータ構造

keyval

ハッシュ表

0 1 2

. . .

H – 1

Page 54: コンピュータアルゴリズム 2 6.  コンテナの実現

typedef struct list_node { struct list_node * next; K key; V val;} * list_node_t;

typedef struct hash_table { int capacity; list_node_t * a;} * hash_table_t;

hash_tablelist_node

Page 55: コンピュータアルゴリズム 2 6.  コンテナの実現

探索・挿入・削除 V search(table, key) {

h = hash(key); return search_list(table->a[h], key);}

insert(table, key, val) { h = hash(key); table->a[h] = cons_list(table->a[h], key, val);}

remove(table, key) { h = hash(key); table->a[h] = remove_list(table->a[h], key);}

Page 56: コンピュータアルゴリズム 2 6.  コンテナの実現

ポイント 多数のキーが十分多くのエントリ (0, 1, …, H – 1) に「ち

らばる」ようにする つまりなるべく「どんなキーが来ても 0, 1, …, H – 1

を一様にとるようなハッシュ関数が望ましい 「最悪」の場合は全部のキーが一つのエントリに固ま

る場合で , 通常の列からの探索と変わらない N 個のデータに対して , O(N) のエントリを用意し , ハッシュ関数がそれらを「均等に」ばらまいてくれるとすると 探索 O(1), 挿入 amortized O(1), 削除 O(1) が達成で

きる

Page 57: コンピュータアルゴリズム 2 6.  コンテナの実現

優先度キュー 以下の操作が可能なコンテナ

key の挿入 (key 間に定義されるある全順序に従った ) 最小 ( 大 )

の key の参照 同,取り出し ( 削除 )

探索はできなくてもいいことに注意

Page 58: コンピュータアルゴリズム 2 6.  コンテナの実現

動機確認 整列されていない配列を用いた場合の計算量

挿入 : O( ) 最小値参照 : O( ) 最小値取り出し : O( )

整列された配列の場合は ? 2分木・平衡木を用いた場合は ? ハッシュ表は役に立たない

Page 59: コンピュータアルゴリズム 2 6.  コンテナの実現

これから述べる優先度キューの計算量 挿入 : O(log n) 最小値参照 : O(1) 最小値取り出し : O(log n)

参照が O(1) であることをのぞけば,平衡木に勝るものではないが,平衡木よりも簡単

Page 60: コンピュータアルゴリズム 2 6.  コンテナの実現

優先度キューのデータ構造:部分順序つき木 (Partially Ordered Tree)

部分順序つき木 構造としては 2 (m)分木と同じだが,不変条件が違う 不変条件 ( 最小値に興味がある場合 ) :

親の key 子の key

2分木よりも自由度が高い 木の形を「規則正しく」維持するのが容易

k k

k

Page 61: コンピュータアルゴリズム 2 6.  コンテナの実現

完全 2分木,ヒープ 完全 2分木

葉以外のすべてのノードの子供の数が 2 n段の完全 2分木のノード数 2n – 1

ヒープ「以下を満たす部分順序つき木」 完全 2分木から 0 個以上の葉を右端から取り除いた

形 任意要素数のヒープがあり,要素数が決まれば形が

一意に決まる

欠けているノード

要素数 12 のヒープ完全 2分木 (4段 )

Page 62: コンピュータアルゴリズム 2 6.  コンテナの実現

ヒープの実際の表現 ( 形があまりに規則的なので ) ポインタを使う必要がな

い 内容 (key) のみを配列に格納 a[i] の左の子 a[2 * i + 1], 右の子 a[2 * i + 2] 注 : 配列の添え字は 0 からとしている

さらなる特典 : 子から親もわかる a[i] の親 a[(i – 1) / 2] (i > 0)

x h

f

ig

e

c

j

k

d

b

a

a x hf igec j kdb

0 1 2 3 4 5 6 7 8 9 10 11

Page 63: コンピュータアルゴリズム 2 6.  コンテナの実現

ヒープに対する操作 : 最小値の参照 根 (a[0]) を見ればよい ! (O(1))

Page 64: コンピュータアルゴリズム 2 6.  コンテナの実現

値の挿入 入れた後の木の形はきまっている

まずは追加されるノードに入れてしまう あとは大小関係を満たすべく要素の入れ替えを行う

9

6

8 9

6

8

交換

新ノード

Page 65: コンピュータアルゴリズム 2 6.  コンテナの実現

最小値 (根 ) の削除 再び,削除後の木の形は決まっている

右下隅を消す (根に移動する ) しかない あとは挿入時と同様のつじつまあわせ

79

6

10

9

6

10 7

9

6

107

Page 66: コンピュータアルゴリズム 2 6.  コンテナの実現

ヒープ操作計算量 挿入・削除

木の深さ分の値の入れ替えしかおこらない 故に O(log n)

Page 67: コンピュータアルゴリズム 2 6.  コンテナの実現

ヒープソート クイズ : ヒープがあれば計算量 O(n log n) で整列ができ

る 2分木の場合と異なり,最悪で O(n log n)