# [資料結構] CH10. Multi-way Search Trees
## M-way search trees
* 在Binary search Tree中,每個node都只會有一個data和兩個pointer。
* 而在M-way search tree的**中,每個node則是有`M-1`個data和`M`個pointer。
* `M`被稱作是這棵M-way search tree的*degree*
* 如果M=2,這顆M-way search tree就等於Binary Search Tree。
```graphviz
digraph Multiway
{
label="3-way search tree"
node[shape=record]
node1 [label="<f0>●|<f1>18|<f2>●|<f3>45|<f4>●"];
node2 [label="<f0>●|<f1>9|<f2>●|<f3>11|<f4>●"];
node3 [label="<f0>●|<f1>27|<f2>●|<f3>36|<f4>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>29|<f2>●|<f3>30|<f4>●"];
node6 [label="<f0>●|<f1>72|<f2>●|<f3>81|<f4>●"];
node1:f0->node2
node1:f2->node3
node1:f4->node4
node3:f2->node5
node4:f4->node6
}
```
* 在每個node中的data,必須按照大小排序。
* 同Binary search Tree,對於每個data `D`,`D`的leftpointer所指向的node中所有data必須小於`D`;rightpointer則反之。
* M-way search tree並不嚴格規定每個node都必須擁有`M-1`筆data。
* 任何一個node的data數可以是`1`~`M-1`。
## B Trees
* B Tree是M-way search tree的一種,但它對於node中的data限制更多。
* 和M-way search tree一樣,一棵degree=`M`的B Tree,每個node最多可擁有`M-1`筆data和`M`個pointer。
* 有些地方定義為 order 而非 degree,請注意自身學習時教學書的用詞。
* 感謝 **skysoul1024** 告知。
* 然而,除了root和leaf之外的所有node,必須至少擁有$\left \lceil \frac{M}{2} \right \rceil$個小孩(pointer)。
* Degree=4,至少有兩個小孩,一筆data。
* Degree=5,至少有三個小孩,兩筆data。
* root至少有兩個小孩。
* 所有葉子必須要在同一個level(同一高度)。
```graphviz
digraph BTree
{
label="B Tree, Degree=4"
node[shape=record]
node1 [label="<f0>●|<f1>45|<f2>●"];
node2 [label="<f0>●|<f1>29|<f2>●|<f3>32|<f4>●"];
node3 [label="<f0>●|<f1>49|<f2>●|<f3>63|<f4>●"];
node4 [label="<f0>●|<f1>18|<f2>●|<f3>27|<f4>●"];
node5 [label="<f0>●|<f1>30|<f2>●|<f3>31|<f4>●"];
node6 [label="<f0>●|<f1>36|<f2>●|<f3>39|<f4>●"];
node7 [label="<f0>●|<f1>46|<f2>●|<f3>47|<f4>●"];
node8 [label="<f0>●|<f1>54|<f2>●|<f3>59|<f4>●|<f5>61|<f6>●"];
node9 [label="<f0>●|<f1>67|<f2>●|<f3>72|<f4>●"];
node1:f0->node2
node1:f2->node3
node2:f0->node4
node2:f2->node5
node2:f4->node6
node3:f0->node7
node3:f2->node8
node3:f4->node9
}
```
### 搜尋
```
在B Tree中搜尋n
1.從root開始,檢查數字n是否在該node中。
2.如果在該node找不到,找出n介於哪兩個數a,b之間,改為拜訪a,b之間的pointer指向的node。
3.若拜訪到的node為NULL,表示找不到該數字。
```
* 在剛剛的範例中搜尋`59`:
```graphviz
digraph BTree
{
label="B Tree, Degree=4"
node[shape=record]
node1 [label="<f0>●|<f1>45|<f2>●"];
node2 [label="<f0>●|<f1>29|<f2>●|<f3>32|<f4>●"];
node3 [label="<f0>●|<f1>49|<f2>●|<f3>63|<f4>●"];
node4 [label="<f0>●|<f1>18|<f2>●|<f3>27|<f4>●"];
node5 [label="<f0>●|<f1>30|<f2>●|<f3>31|<f4>●"];
node6 [label="<f0>●|<f1>36|<f2>●|<f3>39|<f4>●"];
node7 [label="<f0>●|<f1>46|<f2>●|<f3>47|<f4>●"];
node8 [label="<f0>●|<f1>54|<f2>●|<f3>59|<f4>●|<f5>61|<f6>●"];
node9 [label="<f0>●|<f1>67|<f2>●|<f3>72|<f4>●"];
node1:f0->node2
node1:f2->node3
node2:f0->node4
node2:f2->node5
node2:f4->node6
node3:f0->node7
node3:f2->node8
node3:f4->node9
}
```
1.`59`比`45`大,拜訪右子樹
2.`59`介於`49`和`63`之間,拜訪中間那個node。
3.找到`59`ㄌ。
## 插入
* 在B Tree加入新的data時,一定是先放在leaf的地方,如果容量爆了再做處理。
* 一般的Binary Search Tree是向下長,而B Tree是向上長的。
```
1.尋找data應該被放在哪個葉子。
2.如果葉子還沒滿,放入後結束。
3.如果葉子滿了:
a)先把它大力地硬插進去這個node。
b)找到中間值,拆成左邊的node和右邊的node,並把中間值上提拿給老爸。
c)上提後的中間值,左pointer值向剛剛左邊的node,右邊同理。
d)因為老爸多了一個data,檢查老爸有沒有滿(遞迴步驟2or3)。
```
---
* 範例1:在下面這棵B Tree中加入`4`。
* degree=5
```graphviz
digraph BTree
{
node[shape=record]
node1 [label="<f0>●|<f1>18|<f2>●|<f3>45|<f4>●|<f5>72|<f6>●"];
node2 [label="<f0>●|<f1>7|<f2>●|<f3>8|<f4>●|<f5>9|<f6>●|<f7>11|<f8>●"];
node3 [label="<f0>●|<f1>21|<f2>●|<f3>27|<f4>●|<f5>36|<f6>●|<f7>42|<f8>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>81|<f2>●|<f3>89|<f4>●|<f5>90|<f6>●"];
node1:f0->node2
node1:f2->node3
node1:f4->node4
node1:f6->node5
}
```
* 先硬把`4`加進去。
```graphviz
digraph BTree
{
node[shape=record]
node1 [label="<f0>●|<f1>18|<f2>●|<f3>45|<f4>●|<f5>72|<f6>●"];
node2 [label="<f-1>4|<f0>●|<f1>7|<f2>●|<f3>8|<f4>●|<f5>9|<f6>●|<f7>11|<f8>●"];
node3 [label="<f0>●|<f1>21|<f2>●|<f3>27|<f4>●|<f5>36|<f6>●|<f7>42|<f8>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>81|<f2>●|<f3>89|<f4>●|<f5>90|<f6>●"];
node1:f0->node2
node1:f2->node3
node1:f4->node4
node1:f6->node5
}
```
* 容量爆了,將中間值`8`往上提,並拆成左右並將指標指向正確位置。
```graphviz
digraph BTree
{
node[shape=record]
node1 [label="<n2>●|<n1>8|<f0>●|<f1>18|<f2>●|<f3>45|<f4>●|<f5>72|<f6>●"];
node2a [label="<n2>●|<n1>4|<f0>●|<f1>7|<f2>●"];
node2b [label="<f4>●|<f5>9|<f6>●|<f7>11|<f8>●"];
node3 [label="<f0>●|<f1>21|<f2>●|<f3>27|<f4>●|<f5>36|<f6>●|<f7>42|<f8>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>81|<f2>●|<f3>89|<f4>●|<f5>90|<f6>●"];
node1:n2->node2a
node1:f0->node2b
node1:f2->node3
node1:f4->node4
node1:f6->node5
}
```
* 完成。
---
* 範例2:再加入`20`。
* 一樣先硬塞入`20`。
```graphviz
digraph BTree
{
node[shape=record]
node1 [label="<n2>●|<n1>8|<f0>●|<f1>18|<f2>●|<f3>45|<f4>●|<f5>72|<f6>●"];
node2a [label="<n2>●|<n1>4|<f0>●|<f1>7|<f2>●"];
node2b [label="<f4>●|<f5>9|<f6>●|<f7>11|<f8>●"];
node3 [label="<n2>●|<n1>20|<f0>●|<f1>21|<f2>●|<f3>27|<f4>●|<f5>36|<f6>●|<f7>42|<f8>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>81|<f2>●|<f3>89|<f4>●|<f5>90|<f6>●"];
node1:n2->node2a
node1:f0->node2b
node1:f2->node3
node1:f4->node4
node1:f6->node5
}
```
* 中間值`27`往上提,拆左右。
```graphviz
digraph BTree
{
node[shape=record]
node1 [label="<n2>●|<n1>8|<f0>●|<f1>18|<f2>●|<f3>27|<f4>●|<f5>45|<f6>●|<f7>72|<f8>●"];
node2a [label="<n2>●|<n1>4|<f0>●|<f1>7|<f2>●"];
node2b [label="<f4>●|<f5>9|<f6>●|<f7>11|<f8>●"];
node3a [label="<n2>●|<n1>20|<f0>●|<f1>21|<f2>●"];
node3b [label="<f4>●|<f5>36|<f6>●|<f7>42|<f8>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>81|<f2>●|<f3>89|<f4>●|<f5>90|<f6>●"];
node1:n2->node2a
node1:f0->node2b
node1:f2->node3a
node1:f4->node3b
node1:f6->node4
node1:f8->node5
}
```
* 上提後變成爸爸爆炸,只好換爸爸上提拆左右。
```graphviz
digraph BTree
{
node[shape=record]
node0[label="<f0>●|<f1>27|<f2>●"];
node1a [label="<n2>●|<n1>8|<f0>●|<f1>18|<f2>●"];
node1b [label="<f4>●|<f5>45|<f6>●|<f7>72|<f8>●"];
node2a [label="<n2>●|<n1>4|<f0>●|<f1>7|<f2>●"];
node2b [label="<f4>●|<f5>9|<f6>●|<f7>11|<f8>●"];
node3a [label="<n2>●|<n1>20|<f0>●|<f1>21|<f2>●"];
node3b [label="<f4>●|<f5>36|<f6>●|<f7>42|<f8>●"];
node4 [label="<f0>●|<f1>54|<f2>●|<f3>63|<f4>●"];
node5 [label="<f0>●|<f1>81|<f2>●|<f3>89|<f4>●|<f5>90|<f6>●"];
node0:f0->node1a
node0:f2->node1b
node1a:n2->node2a
node1a:f0->node2b
node1a:f2->node3a
node1b:f4->node3b
node1b:f6->node4
node1b:f8->node5
}
```
* 完成。
## 刪除
* 刪除在B Tree裡面就有點麻煩了。
* 我們分成兩種情形來討論:
* 刪除的值不是在葉子(internal node)。
* 刪除的值在葉子(leaf node)。
### Internal Node
* 當刪除的值不在葉子上時,我們先直接將值刪除,然後找**左子樹最大值**代替原來的位置。
* 因為左子樹最大值被拿走了,因此情況就變成了下方的**刪除葉節點**了。
### Leaf node
* 葉節點要考慮的情形就有點多了:
```
1.判斷data數是否低於下限,如果沒有,結束。
2.如果左右兄弟的data數量夠,向和左右的兄弟節點借值來補:
a1)左邊的情形:先找到父親哪個pointer指向自己,將該pointer左邊的data放下來,並將左節點最大值向上推給父親取代。
a2)右邊的情形:先找到父親哪個pointer指向自己,將該pointer右邊的data放下來,並將右節點最小值向上推給父親取代。
3.如果兄弟都沒辦法借,只好向父親求救,一樣方為左右兩左情形:
a1)左邊的情形:先找到父親哪個pointer指向自己,將該pointer左邊的data放下來,然後將自己和左兄弟兩個node合併。
a2)右邊的情形:先找到父親哪個pointer指向自己,將該pointer右邊的data放下來,然後將自己和右兄弟兩個node合併。
b)因為老爸被拿走一個data,回到步驟1但改為確認老爸的節點情況並做處理。
```
* 簡單版:
```
1.刪完沒事就沒事。
2.有事向兄弟求救。
3.兄弟救不了找老爸救。
4.老爸刪完沒事就沒事。
5.老爸有事向老爸的兄弟求救。
6.老爸的兄弟救不了找老爸的老爸。
7.LOOP
```
* 因為該部分步驟過多,用圖片很亂也很累,請參閱下方**其他資源**的部分。
## 其他資源
* [B Tree模擬網頁](https://www.cs.usfca.edu/~galles/visualization/BTree.html)
* 如果不想要手刻一次B Tree,一定要玩玩這個網頁!對插入和刪除的理解都很有幫助。
* 內含動畫,如太快可調整下方**Animation Speed**更改速度。
* [台科大 陳冠宇老師 上課講義](http://faculty.csie.ntust.edu.tw/~kychen/courses/2018_Fall_DS/2018-10-24%20Multi-way%20Search%20Trees.pdf)
* 內含大量例子+圖片,可幫助思考。
###### tags: `DS` `note`