定义。一棵 2-3 查找树 或为一棵空树,或由以下结点构成:![{%}/740943/image01341.gif)
和以前一样,我们将指向一棵空树的链接称为 空链接。2-3 查找树如图 3.3.1 所示。
- 2- 结点,含有一个键(及其对应的值)和两条链接,左链接指向的 2-3 树中的键都小于该结点,右链接指向的 2-3 树中的键都大于该结点。
- 3- 结点,含有两个键(及其对应的值)和三条链接,左链接指向的 2-3 树中的键都小于该结点,中链接指向的 2-3 树中的键都位于该结点的两个键之间,右链接指向的 2-3 树中的键都大于该结点。
命题 F。在一棵大小为 ![N/740943/image00798.gif) 的 2-3 树中,查找和插入操纵访问的结点一定不超过 ![\lg N/740943/image00915.gif) 个。因此我们可以确定 2-3 树在最坏情况下仍有较好的性能。每个操纵中处理每个结点的时间都不会超过一个很小的常数,且这两个操纵都只会访问一条路径上的结点,所以任何查找或者插入的本钱都肯定不会超过对数级别。通过对比图 3.3.11 中的 2-3 树和图 3.2.8 中由相同的键构造的二叉查找树,你也可以看到,完善平衡的 2-3 树要平展得多。比方,含有 10 亿个结点的一棵 2-3 树的高度仅在 19 到 30 之间。我们最多只需要访问30个结点就可以大概在10亿个键中举行任意查找和插入操纵,这是相当惊人的。
证明。一棵含有 ![N/740943/image00798.gif) 个结点的 2-3 树的高度在 ![\lfloor\log_3N\rfloor=\lfloor(\lg N)/(\lg3)\rfloor/740943/image01351.gif)(如果树中满是 3- 结点)和 ![\lfloor\lg N\rfloor/740943/image00945.gif)(如果树中满是 2- 结点)之间(请见训练 3.3.4)。
算法 3.4 红黑树的插入算法图 3.3.24 给出了利用我们的标准索引测试用例举行测试的轨迹和用同一组键按照升序构造一棵红黑树的测试轨迹。仅从红黑树的三种标准操纵的角度分析这些例子对我们理解问题很有帮助,之前我们也是如许做的。另一个基本训练是检查它们和 2-3 树的一一对应关系(可以对比图 3.3.10 中由同一组键构造的 2-3 树)。在两种情况中你都能通过思索 将 P 插入红黑树 所需的转换来检验你对算法的理解水平(请见训练 3.3.12)。除了递归调用后的三条 if 语句,红黑树中 put() 的递归实现和二叉查找树中 put() 的实现完全相同。它们在查找路径上包管了红黑树和 2-3 树的一一对应关系,使得树的平衡性靠近完善。第一条 if 语句会将任意含有红色右链接的 3- 结点(或临时的 4- 结点)向左旋转;第二条 if 语句会将临时的 4- 结点中两条连续红链接中的上层链接向右旋转;第三条 if 语句会举行颜色转换并将红链接在树中向上传递(详情请见正文)。复制代码
- public class RedBlackBST<Key extends Comparable<Key>, Value>
- {
- private Node root;
- private class Node // 含有color变量的Node对象(请见3.3.2.4节)
- private boolean isRed(Node h) // 请见3.3.2.4节
- private Node rotateLeft(Node h) // 请见图3.3.16
- private Node rotateRight(Node h) // 请见图3.3.17
- private void flipColors(Node h) // 请见图3.3.21
- private int size() // 请见算法3.3
- public void put(Key key, Value val)
- { // 查找key,找到则更新其值,否则为它新建一个结点
- root = put(root, key, val);
- root.color = BLACK;
- }
- private Node put(Node h, Key key, Value val)
- {
- if (h == null) // 标准的插入操作,和父结点用红链接相连
- return new Node(key, val, 1, RED);
- int cmp = key.compareTo(h.key);
- if (cmp < 0) h.left = put(h.left, key, val);
- else if (cmp > 0) h.right = put(h.right, key, val);
- else h.val = val;
- if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);
- if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
- if (isRed(h.left) && isRed(h.right)) flipColors(h);
- h.N = size(h.left) + size(h.right) + 1;
- return h;
- }
- }
命题 G。一棵大小为 ![N/740943/image00798.gif) 的红黑树的高度不会超过 ![2\lg N/740943/image01224.gif)。这个上界是比较守旧的。利用随机的键序列和典型应用中常见的键序列举行的实行都证明,在一棵大小为 ![N/740943/image00798.gif) 的红黑树中一次查找所需的比较次数约为(![1.00\lg N-0.5/740943/image01369.gif))。另外,在实际情况下你不太大概遇到比这个数字高得多的均匀比较次数,如表 3.3.1 所示。
简略的证明。红黑树的最坏情况是它所对应的 2-3 树中构成最左边的路径结点全部都是 3- 结点而其余均为 2- 结点。最左边的路径长度是只包罗 2- 结点的路径长度(![\sim\lg N/740943/image00914.gif))的两倍。要按照某种次序构造一棵均匀路径长度为 ![2\lg N/740943/image01224.gif) 的最差红黑树虽然大概,但并不轻易。如果你喜欢数学,你也许会喜欢在训练 3.3.24 中探究这个问题的答案。
命题 H。一棵大小为 ![N/740943/image00798.gif) 的红黑树中,根结点到任意结点的均匀路径长度为 ![\sim1.00\lg N/740943/image01370.gif)。![/740943/image01371.jpeg)
例证。和典型的二叉查找树(比方图 3.2.8 中所示的树)相比,一棵典型的红黑树的平衡性是很好的,比方图 3.3.27 所示(甚至是图 3.3.28 中由升序键列构造的红黑树)。表 3.3.1 显示的数据表明 FrequencyCounter 在运行中构造的红黑树的路径长度(即查找本钱)比初等二叉查找树低 40% 左右,和预期符合。自红黑树的发明以来,无数的实行和实际应用都印证了这种性能改进。
命题 I。在一棵红黑树中,以下操纵在最坏情况下所需的时间是对数级别的:查找( get())、插入( put())、查找最小键、查找最大键、 floor()、 ceiling()、 rank()、 select()、删除最小键( deleteMin())、删除最大键( deleteMax())、删除( delete())和范围查询( range())。各种符号表实现的性能总结如表 3.3.2 所示。
证明。我们已经讨论过 put()、 get() 和 delete() 方法。对于其他方法,代码可以从 3.2 节中照搬(它们不涉及结点颜色)。命题 G 和命题 E 可以包管算法是对数级别的,所有操纵在所颠末的结点上只会举行常数次数的操纵也说明白这一点。
本文由博客一文多发平台 OpenWrite 发布!
欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) | Powered by Discuz! X3.4 |