Balanced Search Trees
平衡搜索树
前面介绍的二叉搜索树在最坏情况下的性能还是很糟糕,而且我们不能控制操作的顺序,有时根本就不是随机的,我们希望找到有更好性能保证的算法。
2-3 search trees
于是先来了解下 2-3 查找树,它可以保证树的平衡性,维护树高在 lgN 级别。这里的 2,3 指的是孩子的数目,图例:
有两个孩子的节点和二叉搜索树一样,节点里有一个键,且大于左子树的键并小于右子树的键。三个孩子的节点里则有两个键,中间孩子的键的大小介于这两个键之间,左右子树一样。
search
查找和二叉查找树一样,虽然现在有的点有两个键,但是也没有什么关系。
查找图例:
insert
插入操作比较关键,解释了为什么可以保证树的平衡性,下面是各种情况的示意:
插入 2-node 时,直接插入把这个节点变成 3-node 即可,上面也没有列出来。插入 3-node 时比较复杂,要先暂时变成 4-node,然后再把三个键中间的键向父母节点转移,问题就又转移到了父母节点上。不难发现,只有在插入路径上全部都是 3-node 时,插入才会让树的高度加一(一路到根节点变成上图情况一)。
而且,上面的操作找到位置后只是改变链接的局部变换,没有数据转移什么的,时间很快,效率挺高。最坏情况下都是 2-node,树高为 lgN,最好情况下都是 3-node,树高为 \(log_{3}N \approx .631 lgN\),反正树高是对数级别,也就保证了查找和插入对数级别的性能。
但是吧,谈到实现,直接实现太复杂,有好多不同类型的节点,还要进行类型转换,而且需要处理的情况也有很多。实现这些不仅需要大量的代码,而且它们产生的额外开销可能会使算法比标准的二叉查找树更慢。我们希望维护树的平衡,同时也希望保障所需的代码能够越少越好。于是乎,红黑树出现啦!
red-black BSTs
红黑树本质上还是二叉树,关键是在标准二叉查找树的基础上添加了一些信息来表示 3-node:
3-node 里的两个键用左斜的红链接连接,较大的键为根,任意的 2-3 树都有唯一的红黑树与之对应:
在这样的表示下,显然不存在有两条红链接的节点,而且任意从根节点到空链接的路径上的黑链接数都是一样的(perfect black balance),还有注意红链接是左连接,在构建的时候要维护这些性质。
每个节点都只有一条来自父母的链接,我们可以借此表示链接的颜色:
代码:
private static final boolean RED = true;
private static final boolean BLACK = false;
private class Node {
Key key;
Value val;
Node left, right;
boolean color; // color of parent link
}
private boolean isRed(Node x) {
if (x == null) return false; // null link is black
return x.color == RED;
}
图例:
红黑树下的查找操作不需要考虑链接的颜色,和二叉查找树一模一样,因为更好的平衡还会快些。
代码:
public Value get(Key key) {
Node x = root;
while (x != null) {
int cmp = key.compareTo(x.key);
if (cmp < 0) x = x.left;
else if (cmp > 0) x = x.right;
else return x.val;
}
return null;
}
其它一些只要比较而不会破坏树结构的顺序相关操作也是,直接用原来二叉查找树的代码就好,主要还是看插入操作。
回想 2-3 树的插入操作,实际上都是直接来,性质被破坏了再调整。像 2-node 就直接变成 3-node,而 3-node 会暂时变成 4-node,然后再去调整。红黑树也是这样,新加入一个节点时,都把新链接认为是红色的,不符合性质再调整,像链接不是左斜或是一个节点有两个红链接。
所以,来了解下为了维护性质的调整操作。
Left rotation
左旋,顾名思义就是把红链接从右斜转到左斜。
Right rotation
右旋和左旋相反,这是一个中间状态,有时候需要先右旋再处理才行,下面会见到。
Color flip
颜色转换,甚至不需要改变任何链接,只要改变颜色就好。
虽然红黑树的插入情况看起来好像很多,但是其实可以用下图来概括(最左边的就要先右旋):
处理也只要统一用下面的代码就好:
private Node put(Node h, Key key, Value val) {
// insert at bottom(and color it red)
if (h == null) return new Node(Key, val, 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); // lean left
if (isRed(h.left) && isRed(h.left.left)) h = rorateRight(h); // balance 4-node
if (isRed(h.left) && isRed(h.right)) flipColors(h);
return h;
}
最后,再来张构造的示例图感受下:
左边一开始的 S,E,A 会形成再上一张图最左边的情况,先对 S 右旋成最右边情况再转换颜色;接下来的 R 很顺利,而 C 会插到 A 的右边形成右斜红链接,需要左旋;H 的插入到 R 的左孩子,这时 S 的左孩子和左孙子的链接都是红色需要右旋,然后 R 两个孩子链接都是红色,转换颜色,之后 E 的右链接变成红色需要左旋...
性能方面,可以保证操作复杂度都是对数级别,一棵大小为 N 的红黑树的高度不会超过 2lgN(证明不管啦)。然后还有其它操作,像删除(更复杂),请参见:RedBlackBST.java。
B-trees(optional)
B 树是一个非常典型的红黑树的实际应用,是平衡树的泛化,每个节点里可以有很多键。因为通常来说,我们需要存储的数据非常大,找到存储数据所在页的时间要比从页里读取数据慢得多,所以我们希望能尽快定位特定的页。B 树每个节点可以有很多很多键,多到可以放一整页的那种:
根节点至少有两个键,内部结点维护键的副本来指导搜索,实际上键有序存储在外部节点上,节点键的数量在 M/2 到 M-1 之间。
查找老样子,来个例子看下:
插入的时候要注意维护树的平衡性,键数目达到 M 的节点需要分裂并向上调整,例图:
因为完美的平衡性,找到特定页的复杂度在 \(log_{M-1}N\) 和 \(log_{M/2}N\) 之间,实际中一般最多只要四次(M =1024; N = 62 billion; \(log_{M/2} \leqslant 4\))。
再贴张 B 树成长的示意图(我都截了):
每一行表示新插入一个键,红色的表示页饱和需要分裂调整。
红黑树被广泛地应用于系统符号表中,Java util 里的 TreeMap 和 TreeSet,C++ STL 里的 map,multimap,multiset 等。
Balanced Search Trees的更多相关文章
- Red Black Tree 红黑树 AVL trees 2-3 trees 2-3-4 trees B-trees Red-black trees Balanced search tree 平衡搜索树
小结: 1.红黑树:典型的用途是实现关联数组 2.旋转 当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质.为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些 ...
- 并不对劲的CF1237D&E:Balanced Playlist and Binary Search Trees
CF1237D Balanced Playlist 题意 有一个长度为\(n\)(\(n\leq 10^5\))的循环播放歌单,每首歌有一个优秀值\(a_i\)(\(a_i\leq 10^9\)). ...
- Method for balancing binary search trees
Method for balancing a binary search tree. A computer implemented method for balancing a binary sear ...
- Optimal binary search trees
问题 该问题的实际应用 Suppose that we are designing a program to translate text from English to French. For ea ...
- [LeetCode] Unique Binary Search Trees 独一无二的二叉搜索树
Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...
- [LeetCode] Unique Binary Search Trees II 独一无二的二叉搜索树之二
Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...
- 2 Unique Binary Search Trees II_Leetcode
Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...
- 【leetcode】Unique Binary Search Trees (#96)
Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...
- LeetCode之Unique Binry Search Trees
4月份很快就过半了,最近都在看WPF,有点落伍了...本来想写一点读书笔记的,还没想好要怎么写.所以为了能够达到每月一篇博客的目标,今天先说一个LeetCode上的面试题:Unique Binary ...
随机推荐
- double转换为int以及浮点型相加损失精度问题
最近在做支付相关模块的业务,数据库字段却使用的是double类型,其实也行,只要计算不在sql语句中进行,也是没有问题的. 预先的类属性设置的是Double类型,自己算的时候发现小数相加会出现损失精度 ...
- python的Web框架:Django路由系统以及模板导入
Django的路由系统 当一个请求来到时 当一个请求来到时 1.首先到项目目录下的urls.py(根URLconf模块)中,查找路由规则: 2.根URELcof模块,里面定义了 urlpatterns ...
- Word文档中多个编号放同一行的方法(非技术)
最近在帮公司出应届生校招面试题,为了方便,选择题部分的答案用了Word的[编号]功能!如下截图所示: 这么简短的四个答案这么竖着放很占空间(打印时也很浪纸张),能不能让它们全部横放在同一行,或两两一组 ...
- oracle 恢复错误修改数据 寒冬冒冷汗!!
今天,由于一时疏忽,造成了对正式数据库修改了用户密码的情况.寒冬冒冷汗!!! 立即上网找修改方法,万幸找到,也修改回来,特此在此留个记忆!! create table t_table_new----这 ...
- Collection集合的带All功能的测试
public class Demo4_CollectionAll { public static void main(String[] args) { // Demo1(); // Demo2(); ...
- Elasticsearch Query DSL 整理总结(三)—— Match Phrase Query 和 Match Phrase Prefix Query
目录 引言 Match Phase Query slop 参数 analyzer 参数 zero terms query Match Phrase 前缀查询 max_expansions 小结 参考文 ...
- 关于如何在本地IIS搭建网站
步骤一: 首先安装与配置IIS服务,可借用百度经验:http://jingyan.baidu.com/article/d5a880eb75f74713f047cc57.html 步骤二: 修改web. ...
- 安装apr-1.6.3报错[cannot remove `libtoolT’: No such file or directory]解决方法
发现有这个提示:cannot remove `libtoolT’: No such file or directory , 编辑 configure文件,查找 $RM "$cfgfile&q ...
- HDU4280(KB11-G 最大流)
Island Transport Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Other ...
- CodeForces822A
A. I'm bored with life time limit per test 1 second memory limit per test 256 megabytes input standa ...