# 【資工考研】DS: Binary Tree (4) AVL Tree + B Tree of Order m
## AVL Tree
我們在[介紹 BST 時](https://hackmd.io/@RyoukiWei/binary_tree_1?stext=23016%3A113%3A0%3A1737374008%3A1_UqSU)說過,BST 之所以 worst case 的時間複雜度會退化成線性跟其高度未平衡有關係
AVL Tree 是 height balanced BST
定義 $H_L$ 跟 $H_R$ 分別為左右子樹的高度,當樹不為空時滿足:
1. $\left| H_L - H_R \right| \le 1$
2. 左右子樹亦為 AVL Tree
AVL tree 控制高度平衡的機制仰賴的便是 balance factor: $H_L - H_R$
其值只會是 $-1,0,1$
透過 balance factor 我們便能在高度不平衡時立刻發現,然後調整
每當我們插入一個新節點,如果會造成不平衡,則依據該新節點位在往左子樹還是往右子樹的方向插,我們可以分成四種情況討論:

AVL tree 的調整仰賴 rotation 的動作,可以分成 right roation 跟 left rotation
以下面這種情況來說:

這個大中小的相對比較是因為 BST 的規則 AVL tree 肯定也是要遵守的
我們要如何對此 right roation 呢?
想像樹節點就像鍊條一樣,我們擺一個三角形台子,right rotation 其實也就是把大中小這一串抓住最上面的節點,把他往右拉
節點會順著三角形台子移動:

這邊的 $t$ 根據 BST 性質,$t \gt x$ 所以當 $x$ 的左右子樹變更時,$t$ 應該會是在 $x$ 的右子樹
又 $t \lt y$ ,所以 $t$ 會在 $y$ 的左子樹
left rotation 也是類似的樣子
前面我們討論的 LL 跟 RR 只需要這樣的 rotation 一次即可
例如 LL 只需要 single right rotation
而 LR 跟 RL 則需要 double rotation
例如 LR 要先抓新節點的父輩做 left rotation ,這樣新節點才會變成 LL 的樣子,這時再 right rotation 即可
輔助的 method :
```cpp!
int getHeight(Node *node) const noexcept { return node ? node->height : 0; }
int getBalanceFactor(Node *node) const noexcept { return node ? getHeight(node->left) - getHeight(node->right) : 0; }
Node *findMin(Node *node) noexcept
{
if (!node)
return node;
Node *curr = node;
while (curr->left)
curr = curr->left;
return curr;
}
void updateHeight(Node *node) const noexcept { node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1; }
```
### rotation
```cpp!
Node *rightRotate(Node *y)
{
// x will become the new root of the subtree
Node *x = y->left;
Node *t = x->right;
// Rotation
x->right = y;
y->left = t;
updateHeight(y);
updateHeight(x);
return x;
}
Node *leftRotate(Node *x)
{
// y will become the new root of the subtree
Node *y = x->right;
Node *t = y->left;
// Rotation
y->left = x;
x->right = t;
updateHeight(x);
updateHeight(y);
return y;
}
```
### Insert
```cpp!
std::pair<Node *, bool> insertUtil(Node *node, const T &val)
{
if (!node)
return {new Node(val), true};
if (val == node->data)
return {node, false};
if (val < node->data)
node->left = insertUtil(node->left, val).first;
else
node->right = insertUtil(node->right, val).first;
updateHeight(node);
const int bf = getBalanceFactor(node);
if (bf > 1 && val != node->left->data)
{
// if LR else LL
if (val > node->left->data)
node->left = leftRotate(node->left);
return {rightRotate(node), true};
}
if (bf < -1 && val != node->right->data)
{
// if RR else RL
if (val < node->right->data)
node->right = rightRotate(node->right);
return {leftRotate(node), true};
}
return {node, true};
}
```
可以看到 LR 跟 RL 需要額外檢查跟一次 rotation 分別變成 LL 跟 RR 後再一次 rotation
### erase
```cpp!
Node *eraseUtil(Node *node, const T &val)
{
if (!node)
return node;
if (val < node->data)
node->left = eraseUtil(node->left, val);
else if (val > node->data)
node->right = eraseUtil(node->right, val);
else
{
if (!node->left && !node->right)
{
Node *temp = node;
node = nullptr;
delete temp;
}
else if (!node->left || !node->right)
{
Node *temp = node->left ? node->left : node->right;
*node = *temp;
delete temp;
}
else
{
Node *successor = findMin(node->right);
node->data = successor->data;
node->right = eraseUtil(node->right, successor->data);
}
}
if (!node)
return node;
updateHeight(node);
const int bf = getBalanceFactor(node);
if (bf > 1)
{
if (getBalanceFactor(node->left) < 0)
node->left = leftRotate(node->left);
return rightRotate(node);
}
if (bf < -1)
{
if (getBalanceFactor(node->right) > 0)
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
```
### 重要性質
對 AVL tree :
- `insert()` 頂多發生 $O(1)$ 次 rotation
- `erase()` 頂多發生 $O(\lg n)$ 次 rotation
- 形成高度 $h$ 的 AVL tree (root level $=1$) 所需的
- 最多節點數: $2^h-1$
- 最少節點數: $F_{h+2}-1$ 其中 $F_n$ 是費氏數 (可以用數學歸納法證)
因為 $\left| F_h \right| + 1\approx \frac{1}{\sqrt 5}(\frac{1 + \sqrt 5}{2})^{h+3}$ (離散的老朋友費氏數列,遞迴式會解吧?)
所以對於 $n$ 個節點的 AVL tree , $h\approx 1.44\lg n$
也就是說 AVL tree 樹高的 worst case 的確還是 $O(\lg n)$ 沒錯
## B Tree of Order m
- Balanced m-way search tree
- 若非空樹,滿足:
- $2 \le \mathrm{deg}(\mathrm{root})\le m$
- $\left\lceil \frac{m}{2} \right\rceil \le \mathrm{deg}(\mathrm{node})\le m, \mathrm{node}\neq\mathrm{root}$
- All external nodes must be the same level (其平衡的意義所在)

[source](http://www.btechsmartclass.com/data_structures/b-trees.html)
> [!Important]
> - The max number of nodes: $\sum\limits_{i=1}^{h}m^{i-1} = \frac{m^h-1}{m-1}$
> - The min number of nodes: $1 + 2\left\lceil \frac{(\left\lceil \frac{m}{2} \right\rceil)^{h-1}-1}{\left\lceil \frac{m}{2} \right\rceil - 1} \right\rceil$
> - The max number of keys: $m^h-1$
> - The min number of keys: $2(\left\lceil \frac{m}{2} \right\rceil)^{h-1}-1$
> [!Tip]
> - B tree of order 3 又叫 2-3 tree
> - B tree of order 4 又叫 2-3-4 tree
### Insert (bottom up)
```pseudocode!
INSERT(T, x):
SEARCH(T, x)
Let p ← parent of the external node where x should be inserted.
INSERT-KEY(p, x)
while number of keys in p > m - 1 do
SPLIT(T, p)
p ← parent of p
```
```pseudocode!
SPLIT(T, p):
Let k ← the ⌈m / 2⌉-th key in p.
Move k to the parent of p.
Create two new children nodes:
Left-child contains keys less than k.
Right-child contains keys greater than k.
Update the parent's child pointers to include the new left and right children.
```
### Insert (top down)
在 insert x 時,search x path 若遇到 m-node ,先 split 再置入到正確的 node 內
### Delte
```
DELETE-BTREE(T, x):
SEARCH-TARGET(T, x)
Let N ← the node containing x.
if N is a leaf then
ERASE-KEY(N, x)
HANDLE-UNDERFLOW(T, N)
else // N is a non-leaf node
Let y ← the predecessor or successor of x (largest key in the left subtree or smallest key in the right subtree).
REPLACE-KEY(N, x, y)
DELETE-BTREE(T, y)
```
```
HANDLE-UNDERFLOW(T, N):
// safe
if number of keys in N ≥ ⌈m / 2⌉ - 1 then
return
else
if N has a sibling with extra keys (rotation is possible) then
PERFORM-ROTATION(T, N)
else // Merge is necessary
PERFORM-MERGE(T, N)
Let P ← parent of N.
HANDLE-UNDERFLOW(T, P)
```
```
PERFORM-ROTATION(T, N):
Let S ← a sibling of N with extra keys.
if S is to the left of N then
Move the largest key in S to the parent of N.
Move the parent's separating key to N.
else // S is to the right of N
Move the smallest key in S to the parent of N.
Move the parent's separating key to N.
```
```
PERFORM-MERGE(T, N):
Let S ← a sibling of N.
Let P ← parent of N.
Merge N and S into a single node, including the separating key from P.
Remove the separating key from P.
```
> [!Warning]
> 如果 merge 後 parent is underflow
> 要切斷所有這代父輩與其子輩們的 link ,parent 做 rotation 若真的不行再 merge
> 等父輩們都搞定了再重建關係
> [!Important]
> 這邊的 rotation 與 AVL tree 的不同喔
### B+ Tree of Order m
支援 index sequential access method
主要分兩層:
- index level: 使用 B tree of order m ,純粹用來做**索引**,不用來存放 data
- data blocks level: 存 data 用的,各個 blocks 間以 linked list 的方式串接
## 【資工考研】DS: Binary Tree 全系列
- [【資工考研】DS: Binary Tree (1)](https://hackmd.io/@RyoukiWei/binary_tree_1)
- [【資工考研】DS: Binary Tree (2)](https://hackmd.io/@RyoukiWei/binary_tree_2)
- [【資工考研】DS: Binary Tree (3)](https://hackmd.io/@RyoukiWei/binary_tree_3)
- [【資工考研】DS: Binary Tree (4)](https://hackmd.io/@RyoukiWei/binary_tree_4)
- [【資工考研】DS: Binary Tree (5)](https://hackmd.io/@RyoukiWei/binary_tree_5)