「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+...

50
探索 (2) — 探索木 田浦健次朗 1 / 38

Transcript of 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+...

Page 1: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

探索 (2) — 探索木

田浦健次朗

1 / 38

Page 2: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木▶ アイデア: ≈ 二分探索 + 探索時のデータ構造の書き換えが少なくなるデータ構造

3

2 8

5

19

15

23

27

12

29

2 3 5 8 12 15 19 23 27 29

配列(二分探索)

木(二分探索木)

2 / 38

Page 3: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木: データ構造木構造の一種で,

▶ 各頂点に ⟨ 鍵, 値 ⟩ を一つ格納▶ 各頂点に≤ 2個の子供�

1 typedef struct bintree_node {2 K key; // 鍵3 V val; // 値4 struct bintree_node * left;5 struct bintree_node * right;6 } bintree_node;

注: 図では val を省略

3

2 8

5

15

19

23

27

12

29

key: 23

left: right:

データ構造の不変条件: 各ノード n に対し,n の左の子孫の鍵 < n の鍵 < n の右の子孫の鍵

3 / 38

Page 4: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木: データ構造の細かい注意

▶ 各頂点に ⟨ 鍵, 値 ⟩ を一つ格納だったら, 空の木はどう表される?

▶ 必然的に頂点が 0 個▶ 根へのポインタを持つ別の構造体を作る

▶ 空 ⇐⇒ 根へのポインタがNULL�

1 typedef struct bintree_map {2 bintree_node * root;3 } bintree_map;

3

2 8

5

15

19

23

27

12

29

root:

root:

要素数0

要素数>0

4 / 38

Page 5: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木の探索 (find)▶ データ構造の不変条件から, やるべきことは自ずと決まる

▶ 目当ての鍵がそのノードの鍵より小さければ左, 大きければ右�

1 V bt_find(bintree_node * n,2 K key, V nf) {3 if (n == NULL) return nf;4 if (key == n->key)5 return n->val;6 if (key < n->key)7 return bt_find(n->left, key, nf);8 if (key > n->key)9 return bt_find(n->right, key, nf);

10 }

3

2 8

5

15

19

23

27

12

29

12<14

14<23

14?

14<15

5 / 38

Page 6: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木への要素追加 (add)やはりデータ構造の不変条件から (木の形を大幅に変えない限り), 「誰の子になるべきか」は自ずと決まる�

1 void bt_add(bintree_node * n,2 K key, V val) {3 if (key == n->key) {4 n->key = key; n->val = val;5 } else if (key < n->key) {6 if (n->left)7 bt_add(n->left, key, val);8 else9 n->left = bt_mk_node(key, val);

10 } else {11 if (n->right)12 bt_add(n->right, key, val);13 else14 n->right = bt_mk_node(key, val);15 }16 }

3

2 8

5

15

19

23

27

12

29

3<4

4<12

add 4

4<8

4

4<5

6 / 38

Page 7: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)

1. 削除すべきノードを見つける2. その後, 木の形をどう再整理すべきか?2.1 子が 0 個のノード (葉) の場合

(例: 図の 29)2.2 子が 1 個の場合 (例: 図の 8,

15)2.3 子が 2 個の場合 (例: 図の 23)

3. 「データ構造の不変条件」を保つように

3

2 8

5

15

19

23

27

12

29

4

7 / 38

Page 8: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)削除されるノードを n として

▶ n の子が 0 個: n の親 → NULL とするだけ

3

2 8

5

15

19

23

27

12

29

4

3

2 8

5

15

19

23

27

12

4

29

▶ n の子が 1 個: n の親 → n の (唯一の) 子供とする

3

2 8

5

15

19

23

27

12

29

4

3

2 8

5

15

19

23

27

12

29

4

8 / 38

Page 9: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)n の子が 2 個の場合

▶ データ構造の不変条件を保つには, 木をどう書き換えたら良いか?

3

2 8

5

15

19

23

27

12

29

4

9 / 38

Page 10: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)▶ 削除されるノード n に子が 2 個 (l と r) いるとする▶ ポイント: データ構造の不変条件を壊さない ⇒

▶ 「l の子孫の鍵 < k′ < r の子孫の鍵」となる新しい k′を見つける必要がある

▶ どこから?

1. l のどこかから ⇒ l の中の最大値, または2. r のどこかから ⇒ r の中の最小値 (どちらでも可)

3

2 8

5

15

19

23

27

12

29

4

10 / 38

Page 11: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)▶ 削除されるノード n に子が 2 個 (l と r) いるとする▶ ポイント: データ構造の不変条件を壊さない ⇒

▶ 「l の子孫の鍵 < k′ < r の子孫の鍵」となる新しい k′を見つける必要がある

▶ どこから?

1. l のどこかから ⇒ l の中の最大値, または2. r のどこかから ⇒ r の中の最小値 (どちらでも可)

3

2 8

5

15

19

23

27

12

29

4

10 / 38

Page 12: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)▶ 削除されるノード n に子が 2 個 (l と r) いるとする▶ ポイント: データ構造の不変条件を壊さない ⇒

▶ 「l の子孫の鍵 < k′ < r の子孫の鍵」となる新しい k′を見つける必要がある

▶ どこから?

1. l のどこかから ⇒ l の中の最大値, または2. r のどこかから ⇒ r の中の最小値 (どちらでも可)

3

2 8

5

15

19

23

27

12

29

4

10 / 38

Page 13: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除 (del)▶ 削除されるノード n に子が 2 個 (l と r) いるとする▶ ポイント: データ構造の不変条件を壊さない ⇒

▶ 「l の子孫の鍵 < k′ < r の子孫の鍵」となる新しい k′を見つける必要がある

▶ どこから?1. l のどこかから ⇒ l の中の最大値, または2. r のどこかから ⇒ r の中の最小値 (どちらでも可)

3

2 8

5

15

19

23

27

12

29

4

10 / 38

Page 14: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除: プログラム▶ n′ = bt_del(n, k) ⇒ k を削除後の木の根 (n の位置に収まるべきノード) を返す

▶ k は存在しているという前提 (事前に検査しておけばよい)�

1 bintree_node * bt_del(bintree_node * n, K key) {2 if (key < n->key) {3 n->left = bt_del(n->left, key); return n;4 } else if (key > n->key) {5 n->right = bt_del(n->right, key); return n;6 } else {7 bintree_node * left = n->left, * right = n->right;8 free(n);9 if (!left) return right;

10 if (!right) return left;11 bintree_node * x;12 /* 右の子の最小値を削除 */13 bintree_node * r = bt_del_min(right, &x);14 x->left = left;15 x->right = r;16 return x;17 }18 }

11 / 38

Page 15: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木からの要素削除: 最小値の削除▶ bt_del_min(n, x)

▶ n の最小の鍵を削除▶ 削除後の根ノード (新たに n の位置に収まるノード) を返す

▶ *x に, 削除されたノードを格納�1 bintree_node * bt_del_min(bintree_node * n,2 bintree_node ** x) {3 if (n->left) {4 n->left = bt_del_min(n->left, x);5 return n;6 } else {7 *x = n;8 return n->right;9 }

10 }

12 / 38

Page 16: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木の計算量

▶ n 要素に対して,▶ find, add, del の計算量 = 木の深さ

▶ 木の深さ ∈ O(log n) を期待しているのだがそうだろうか?

▶ 挿入された要素が左右均等に散ってくれればそうだろう

▶ 最悪の場合は, 否

13 / 38

Page 17: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木の計算量

▶ n 要素に対して,▶ find, add, del の計算量 = 木の深さ

▶ 木の深さ ∈ O(log n) を期待しているのだがそうだろうか?

▶ 挿入された要素が左右均等に散ってくれればそうだろう

▶ 最悪の場合は, 否

13 / 38

Page 18: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木の計算量

▶ n 要素に対して,▶ find, add, del の計算量 = 木の深さ

▶ 木の深さ ∈ O(log n) を期待しているのだがそうだろうか?

▶ 挿入された要素が左右均等に散ってくれればそうだろう

▶ 最悪の場合は, 否

24

10 36

28

68

72

78

83

50

91

10

24

28

36

68

72

78

83

91

13 / 38

Page 19: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

ここまで

線形探索 二分探索 二分探索木 (平均/最悪)†find O(n) O(log n) O(log n)/O(n)add O(n) O(n) O(log n)/O(n)del O(n) O(n) O(log n)/O(n)

▶ 注: 平均 † は, 証明したわけではない

14 / 38

Page 20: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

平衡木

▶ 普通の二分探索木の問題点1. 木の形が偏る2. 木の深さが ∈ O(log n) とならない (n は要素数)3. 探索 ・追加 ・削除の計算量が ∈ O(log n) とならない

▶ 「深さが偏らない」木 (平衡木) を作りたい▶ 代表的な平衡木

▶ AVL 木▶ 二分木▶ |左右の深さの差| ≤ 1

▶ B 木, B+ 木▶ 子供の数を「ある範囲」に保つ▶ その下で, どこも深さが同じ

15 / 38

Page 21: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

m 分探索木▶ 二分探索木の子供の数を一般化したもの▶ 例: 四分探索木

▶ 4 つまでの子を持つ▶ 子が n 個のとき, (n − 1) の鍵を持つ

16 / 38

Page 22: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B 木 (B-tree)

▶ m を 3 以上の整数とする▶ 以下を満たす m 分探索木を次数 m の B 木という

1. 根, 葉以外の頂点は, ⌈m/2⌉ 本以上 m 本以下の子を持つ

2. 根から葉に至る道の長さはどれも同じ▶ 原典: R. Bayer and E. McCreight. Organization and

Maintenance of Large Ordered Indexes. ActaInformatica 1, 173-189 (1972).

17 / 38

Page 23: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木 (B+tree)B 木の変形で,

1. ⟨ 鍵, 値 ⟩ は葉のみに格納する2. 葉でない頂点 (内部頂点) には, 値を格納せず, 子供の鍵の境界値のみ格納

18 / 38

Page 24: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

次数 3 の B+ 木の例

▶ 子ノードの数: 2 または 3 (⌈3/2⌉ = 2)

2 8 13 19 21 28 33 41 48 53 67 80

8 13

19

21

28

33 48 53 80

67

41

19 / 38

Page 25: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B 木, B+ 木の図解, アニメーション

▶ B 木:▶ http://slady.net/java/bt/view.php

▶ B+ 木:▶ http://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

▶ 以降の説明は B+ 木について行う▶ 基本的考え方は共通; 「B 木」と言った場合, B 木, B+木共通の説明

20 / 38

Page 26: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

実践的な B+ 木

1. m をうまく調節する▶ m が大: 浅い木構造, 一個のノードの大きさが大きくなる▶ m が小: 深い木構造, 一個のノードの大きさが小さくなる

2. データベースやファイルシステムなど, 2 次記憶での検索用途にしばしば用いられる

3. その場合, ノード一つの大きさは大きめ (> 数 KB) とする (m = 数百)

4. 葉も同じくらいの大きさにして, 複数 (⌈l/2⌉ 個以上 l 個以下) の ⟨ 鍵, 値 ⟩ の組を格納

21 / 38

Page 27: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木ノードの定義�1 typedef struct btree_node {2 int kind; /* 葉か, 内部ノードか */3 union {4 /* 葉 : l 個以下の ⟨ 鍵, 値 ⟩ の配列 */5 struct {6 int nk;7 KV kvs[l+1]; };8 /* 内部 : m 個以下の子へのポインタ9 (m − 1) 個以下の分割鍵

10 c0 < k0 ≤ c1 < k1 ≤ . . . < knc−2 ≤ cnc−1 */11 struct {12 int nch; /* 子の数 */13 K ks[m];14 struct btree_node * c[m+1]; };15 };16 } btree_node;

▶ 葉の場合と, 内部ノードの場合の共用体

▶ 配列は全て, 1 要素分余計に持つ. 理由は後述.

葉 内部22 / 38

Page 28: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木全体の定義�1 typedef struct btree_map {2 btree_node * root;3 } btree_map;

▶ 木の根へのポインタを持つ構造体

▶ 根ノードが挿入 ・削除によって入れ替わることに対処

2 8 13 19 21 28 33 41 48 53 67 80

8 13

19

21

28

33 48 53 80

67

41

root

23 / 38

Page 29: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B 木の基本的考え方なぜ B 木は「偏らない」のか?

▶ 二分探索木では, 木は「下へ伸びる」▶ ノードは下へ追加される▶ 親子関係 → 追加された順序

▶ B 木では, 木は「横へ伸ばす」▶ 横へ広がり過ぎたら, 木を全体で「一段」増やす

二分探索木

B木

24 / 38

Page 30: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木の探索 (find)▶ データ構造の不変条件はほとんど同じなので, ほとんど二分探索木の探索と同じ

▶ 細かい違い:▶ 子供は 2 つとは限らない▶ B+ 木では, 途中 (内部ノード) で検索が終わることはない. 必ず葉まで降りる

▶ 下図では葉には一要素しかないが, 実際にはそこでまた所望のデータを (線形または二分) 探索

2 8 13 19 21 28 33 41 48 53 67 80

8 13

19

21

28

33 48 53 80

67

41

root28?

25 / 38

Page 31: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木の探索 (find) プログラム�1 V btr_find(btree_node * n, K key, V nf) {2 if (n->kind == leaf) {3 /* 配列kvs から key を探索 */4 return kvs_array_find(n->kvs, n->nk, key, nf);5 } else {6 /* ks[i − 1] ≤ key < ks[i]を満たすi */7 int i = ks_array_find(n->ks, n->nc - 1, key);8 return btr_find(n->c[i], key, nf);9 }

10 }11 V btree_find(btree_map * m, K key, V nf) {12 if (!m->root) return not_found;13 else return btr_find(m->root, key, nf); }

▶ kvs_array_find, ks_array_find は線形もしくは二分探索で容易に実現できる

▶ l や m が非常に小さい (例えば < 10) うちは線形で十分26 / 38

Page 32: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: 方針1. find と同じ要領で「鍵が追加されるべき葉ノード n」を探索, そこへ追加

2. n が溢れなければ (追加後の要素数 ≤ l), 終了3. 溢れたら,

3.1 (split) n の「弟」n′ を作り, (l + 1) 個の要素を分割3.2 結果, n の親 p に子供が増える3.3 p が溢れなければ (子供追加後の要素数 ≤ m), 終了3.4 溢れたら,

3.4.1 p の「弟」p′ を作り, (m + 1) 個の子を半分こ!3.4.2 結果, p の親 g に子供が増える3.4.3 g が溢れなければ (子供追加後の要素数 ≤ m), 終了3.4.4 溢れたら, …(以下同様)

4. そうこうして根が溢れたら, 根を分割して新しい根を作る(木の高さ +1)

27 / 38

Page 33: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: 図解m = 2, l = 1. 1 を加えるとする

2 8 13 19 21 28 33

8 13

19

21

28

33

root

1

28 / 38

Page 34: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: 図解まずは葉に追加 (配列が 1 要素余分にとってある理由)

2 8 13 19 21 28 33

8 13

19

21

28

33

root

2

1

28 / 38

Page 35: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: 図解葉があふれたので分割. 親の子が増える

2 8 13 19 21 28 33

13

19

21

28

33

root

2

1

8

28 / 38

Page 36: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: 図解親もあふれたので分割

2 8 13 19 21 28 33

13

19

21

28

33

root

2

1

8

28 / 38

Page 37: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: プログラム (1)▶ B 木のノード n の下に key → val を追加または上書き�

1 int btr_add(btree_node * n, K key, V val) {2 if (n->kind == leaf) {3 return btr_add_kv(n, key, val);4 } else {5 int i = ks_array_find(n->ks, n->nch - 1, key);6 btree_node * c = n->c[i];7 int x = btr_add(c, key, val);8 if (overflow(c)) {9 /* 分割後の 2つのノード (l,r)とその間の分割鍵k

10 (実際のC では構造体) */11 l,r,k = split(c);12 btr_add_child(n, i + 1, r, k);13 }14 return x;15 }16 }

29 / 38

Page 38: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木への追加: プログラム (2)btree_map 全体に対する追加�

1 void btree_add(btree_map * m, K key, V val) {2 if (!m->root) {3 m->root = mk_leaf1(key, val);4 } else {5 btree_node * r = m->root;6 long x = btr_add(r, key, val);7 if (overflow(r)) {8 l,r,k = split(r);9 m->root = mk_node2(l, r, k);

10 }11 }12 }

30 / 38

Page 39: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: 方針1. find の要領で「鍵を含む葉ノード n」を探索, そこから削除2. n がunderflow(追加後の要素数 < l/2)しなければ終了3. underflow したら,

3.1 (balance/merge) n の「兄または弟」n′ との間でやりくり3.2 n, n′ とも underflow しないように出来れば終了3.3 n, n′ とも underflow する (n′ の要素数 = l/2) 場合, 両

者を合併3.4 結果, n の親 p の子供が減る3.5 p が underflow したら,

3.5.1 p の「兄または弟」p′ との間でやりくり3.5.2 p, p′ とも underflow しないように出来れば終了3.5.3 p, p′ とも underflow する(p′ の子の数 = m/2)場合, 両

者を合併3.5.4 結果, p の親 g の子供が減る3.5.5 以下同様…

4. そうこうして根の子が一人になったら, その子供が根になる (木の高さ −1) 31 / 38

Page 40: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: 図解m = 2, l = 1. 21 を削除するとする

2 8 13 19 21 28 33

13

19

21

28

33

root

2

1

8

32 / 38

Page 41: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: 図解まずは葉を削除

2 8 13 19 28 33

13

19

21

28

33

root

2

1

8

32 / 38

Page 42: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: 図解子の数が一人に. 隣とやりくり (合併)

2 8 13 19 28 33

13

19

21

28

33

root

2

1

8

32 / 38

Page 43: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: 図解また子の数が一人に. 隣とやりくり (合併)

2 8 13 19 28 33

13

19

21

28

33

root

2

1

8

32 / 38

Page 44: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: 図解根の子供が一人になった. その子が根に

2 8 13 19 28 33

13 21

28

33

root

2

1

8

32 / 38

Page 45: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: プログラム (1)�1 int btr_del(btree_node * n, K key) {2 if (n->kind == leaf) {3 return btr_del_kv(n, key);4 } else {5 K * ks = n->ks; btree_node ** c = n->c;6 int i = ks_array_find(ks, n->nch - 1, key);7 int x = btr_del(c[i], key);8 if (underflow(c[i])) {9 if (i == n->nch - 1) { // 右端の子. 兄とやりくり

10 balance_or_merge(n, i-1, c[i-1], c[i], ks[i-1]);11 } else { // それ以外. 弟とやりくり12 balance_or_merge(n, i, c[i], c[i+1], ks[i]);13 }14 }15 return x;16 }17 }

33 / 38

Page 46: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木からの削除: プログラム (2)�1 int btree_del(btree_map * m, K key) {2 if (!m->root) return 0;3 btree_node * r = m->root;4 int x = btr_del(r, key);5 // underflow 時の処理. 根だけ少し特別6 if (r->kind == leaf) {7 if (r->nk == 0) {8 free(r); m->root = NULL;9 }

10 } else {11 if (r->nch == 1) {12 btree_node * c = r->c[0];13 free(r); m->root = c;14 }15 }16 return x;17 }

34 / 38

Page 47: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

B+ 木の計算量

ポイント: 「平衡」しているので, 深さが ≈ log n で抑えられる1. 深さ d の B+ 木の葉ノード数 ≥ 2d

2. ∴ n 要素の B+ 木の深さ ≤ log n3. find, add, del とも, 再帰呼び出し以外のコストはデータの大きさによらない ≤ a

4.∴各操作の計算量 ≤ a ·深さ ∈ O(log n)

35 / 38

Page 48: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

ここまで

線形探索 二分探索 二分探索木 (平均/最悪)† B(+) 木find O(n) O(log n) O(log n)/O(n) O(log n)add O(n) O(n) O(log n)/O(n) O(log n)del O(n) O(n) O(log n)/O(n) O(log n)

▶ 注: 平均 † は, 証明したわけではない

36 / 38

Page 49: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木 ・ B 木を利用した整列▶ 二分探索木は要素を小さい順に列挙することが自然にできる�

1 visit(node * n) {2 if (n->left) visit(n->left)3 process(n->key);4 if (n->right) visit(n->right)5 }

▶ また, 「最小値を返す」ことも容易にできる�1 void find_min(node * n) {2 if (n->left) return find_min(n->left)3 return n->key;4 }

▶ B 木も同様

3

2 8

5

19

15

23

27

12

29

37 / 38

Page 50: 「膣(2) 「膣∽┬ - 東京大学tau/lecture/...Informatica 1, 173-189 (1972). 17/38 B+ 木(B+tree) B 木の変形で, 1. 鍵, 値 は葉のみに格納する 2. 葉でない頂点(内部頂点)

二分探索木 ・ B 木を利用した整列

▶ これらを用いて「整列アルゴリズム」が簡単にできる�1 m = bintree_create();2 for (i = 0; i < n; i++) bintree_add(m, a[i]);3 for (i = 0; i < n; i++) {4 a[i] = find_min(m); bintree_del(m, a[i]);5 }

▶ その整列アルゴリズムの計算量 (B 木の場合):∈ O(log n × n)

▶ 整列アルゴリズムとして特段魅力的ではないが, 有用な考え方

38 / 38