平衡查找树
B-Tree:
B-trees也被称为2-3-4/2-3 trees. 数字代表每个节点可以拥有的子节点数. 例如一个2-3-4树可能由以下节点构成:
- 2-节点: 1个元素和2个儿子
- 3-节点: 2个元素和3个儿子
- 4-节点: 3个元素和4个儿子
因此当2-3-4棵树的节点有3个元素并需要添加另外一个元素时,它会分裂节点以满足上述要求.
B-Tree添加元素:
添加元素到2-3-4树的过程是:
-
首先将元素插入到一个叶子节点中,即首先沿着树向下遍历,根据要插入的元素是否大于或小于每个节点中的元素,向左或向右移动直至叶子节点。
-
将元素添加到叶节点之后,如果新节点有4个元素,则弹出middle left元素并相应地重新排列子节点。
-
如果这导致父节点有4个元素,那么再次弹出middle left元素,相应地重新排列子节点。
-
重复这个过程,直到父节点能够容纳弹出的元素/或者到达根节点。
如下图所示:
B-Tree可视化网站: https://www.cs.usfca.edu/~galles/visualization/BTree.html
B-Tree删除元素:
B-Tree的删除操作比较复杂, 但有迹可循, 可参考这篇slides
B-Tree的旋转:
尽管 B 树做到了平衡,但实现起来困难。需要跟踪不同的节点,而且分割过程非常复杂。旋转(Rotate)是另一种方法来创建一个平衡的树。
旋转的定义是:
rotateLeft(G): Let x be the right child of G. Make G the new left child of x, 即把去除右子树的G作为G的右子树的左子树
rotateRight(G): Let x be the left child of G. Make G the new right child of x.
利用旋转来平衡树的一个Demo.
private Node rotateRight(Node h) {
// assert (h != null) && isRed(h.left);
Node x = h.left;
h.left = x.right;
x.right = h;
return x;
}
// make a right-leaning link lean to the left
private Node rotateLeft(Node h) {
// assert (h != null) && isRed(h.right);
Node x = h.right;
h.right = x.left;
x.left = h;
return x;
}
红黑二叉查找树:
2-3树总是保持平衡, 但很难实现. 为什么不创建一个使用 BST 实现的树, 但在结构上与2-3树相同的数据结构呢?
接下来以2-3树为例, 看看需要做什么修改能将其转换为一个BST:
- 对于只有两个儿子的2-3树, 其已经是BST, 因此无需做任何修改
- 当有三个儿子时呢?
我们选择左元素作为右元素的子元素,这导致了一个"左倾"的树, 我们通过把一个链接变成红色来说明这种情况。而正常链接是黑色的。因此,我们称这些结构为左倾红黑树(LLRB)。
左倾红黑树与2-3棵树有1-1对应关系。每个2-3树有一个唯一的 LLRB 红黑树与之相关联。至于2-3-4树,它们与标准的红黑树保持一致。
从上面可以看出: 红黑二叉树的思想使用标准的二叉查找树(完全由2-节点构成)和一些额外的信息(替换3-节点)来表示2-3树,红链接将两个2-节点连接起来构成一个3-节点,黑链接则是2-3树中的普通链接。确切地说,我们将3-节点表示为一条左斜的红色连接相连的两个2-节点(两个2-节点之一是另一个的左子节点),如下图所示。
![image-20220819142320838](https://i-blog.csdnimg.cn/blog_migrate/1e0d2da6a9470ae72a76400cb143077d.png)
LLRB红黑树的另一种定义:
- 红链接均为左链接
- 没有任何一个节点同时和两条红链接相连
- 该树是完美黑色平衡 的,即任意空连接到根节点的路径上的黑链接数量相同
关于红黑树的其他操作参考《算法4》Page-276