希望找到一种数据结构,能够保证所有操作(范围查找除外)均能够在对数时间内完成操作,以便改变二叉查找树的不足。
平衡查找树Balance Search Tree
一.2-3查找树
1.数据结构:每个结点允许1个或者两个key
(1)2-node:一个键,两个child。左子树小于该节点,右子树大于该节点。
(2)3-node:两个键,三个child。左子树小于两个节点,右子树大于两个节点,中间的子树在两个节点之间。
这个限定称为symmetric order
2.完美平衡(Perfect balance):所有空链接到根结点的距离应该相等。
3.查找的思路:根据2-3查找树的概念,从根开始递归的查找该元素。和二叉查找树类似。
4.插入的思路:(保持完美平衡和symmetric order)
(1)如果在底部的2-node中插入,查找key应该插入的位置,然后将2-node变为3-node
(2)如果在底部的3-node中插入
1)在3-node中增加一个key,暂时形成4-node
2)将4-node中中间的一个移动到父结点中
3)如果不满足2-3的结构的话,重复上面的过程
4)如果已经到达了根结点,并且根结点是一个4-node,则将它拆分为3个2-node,也就是使得树的层次+1
5.三种情况举例:
(1)插入K(2-node变为3-node)
-------------》
(2)插入Z(临时4-node移动到父结点)
--------------------》
(3)插入L(根结点都满了,增加树的高度)
--------》------》
6.说明:
(1)树的高度。最坏的情况,全为2-nodes,性能是lgN。最好的情况,全为3-nodes,性能是logN/log3。
(2)对于million结点来说,树的高度在12到20之间。对于billion结点来说,高度在18到30之间。
(3)可以保证clgN的性能。
(4)直接的实现很复杂
二.红黑二叉查找树red-black BSTs
1.基本思想:利用标准的二叉查找树和一些额外的信息来表示2-3树
(1)红链接将2-node连接起来构成3-node
(2)黑链接则是2-3树中的普通链接
(3)3-node中较大的key是root
2.另一个等价的定义:红黑树(left-leaning red-black BST)是一个含有红黑链接的特殊的二叉查找树,满足:
(1)没有一个结点同时和两个红链接相连
(2)该树是完美黑色平衡。即任意空链接到根结点的路径上的黑链接数量相同
(3)红链接均为左链接。
3.从上面的分析可以,红黑树是 2-3树的一种等价的简单的实现。
4.查找。和普通的BST类似(不考虑颜色),但是由于具有更好的平衡性,红黑树有更快的查找速度。
很多其他的操作(ceiling,selection等),也和普通的BST类似。
5.用BST表示红黑树。颜色表示,在普通BST的实现中给每个Node加上一个颜色(布尔值),表示父链接的颜色是红(true)/黑(false)。
6.左旋转。将一个右倾斜的红链接变为左倾斜。依旧维持了symmetric order和完美黑色平衡。
--------------------》
//左旋转,将右倾斜变为左倾斜 Node rotateLeft(Node h) { //结点关系 Node x=h.right; h.right=x.left; x.left=h; //颜色 x.color=h.color; h.color=RED; return x; }
7.右旋转。作为过渡的步骤。
---------------》
//右旋转 Node rotateRight(Node h) { Node x=h.left; h.left=x.right; x.right=h; x.color=h.color; h.color=RED; return x; }
8.颜色反转。重绘颜色来分开一个临时的4-node。
----------------》
private void flipColors(Node h) { h.color=RED; h.left.color=BLACK; h.right.color=BLACK; }
9.插入。通过上述基本操作可以使得2-3树和红黑树一一对应。
向一个3-node中插入新键的三种情况(算法的基础)
10.例子(完整的例子见算法P282)
---》-->-->-->
11.插入的思路:(以下结论对上述讨论的所有情况均满足)
(1)右边child是红色,左边child是黑色:左旋转
(2)左边child,左左边grandchild均为红色:右旋转
(3)children均是红色:颜色反转
12.插入操作的代码实现
package com.cx.serch; public class RedBlackBST <Key extends Comparable<Key>,Value>{ private Node root; private static final boolean RED=true; private static final boolean BLACK=false; private class Node{ Key key; Value value; Node left,right; int count; //父链接的颜色 boolean color; public Node(Key key,Value value,int count,boolean color) { this.key=key; this.value=value; this.count=count; this.color=color; } } /** * 实现size()方法 */ public int size() { return size(root); } private int size(Node x) { if(x==null) return 0; return x.count; } /** * 实现put()方法 */ public void put(Key key,Value value) { root=put(root, key, value); root.color=BLACK; } //插入元素操作 private Node put(Node h,Key key,Value value) { //在底层插入时,先插入红链接 if(h==null) return new Node(key,value,1,RED); //普通BST的插入操作 int cmp=key.compareTo(h.key); if(cmp<0) h.left=put(h.left, key, value); else if(cmp>0) h.right=put(h.right, key, value); else h.value=value; //右子结点是红色,左子结点是黑色,进行左旋转 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.count=1+size(h.left)+size(h.right); return h; } private boolean isRed(Node x) { if(x==null) return false; return x.color==RED; } //左旋转,将右倾斜变为左倾斜 Node rotateLeft(Node h) { //结点关系 Node x=h.right; h.right=x.left; x.left=h; //颜色 x.color=h.color; h.color=RED; return x; } //右旋转 Node rotateRight(Node h) { Node x=h.left; h.left=x.right; x.right=h; x.color=h.color; h.color=RED; return x; } //反转颜色 private void flipColors(Node h) { h.color=RED; h.left.color=BLACK; h.right.color=BLACK; } }
13.说明
(1)红黑树在最坏情况下,树的高度不大于2lgN
(2)在应用中,红黑树的盖度~1.00lgN
(3)具有lgN的性能保障
14.红黑树的用途:
(1)Java:java.util.TreeMap,java.util.TreeSet
(2)C++ STL:map,multimap,mulyiset
(3)Linux内核:完全公平调度
三.B树
1.用在数据量较大,外存占主要部分时,B树因其读磁盘次数少,而具有更快的速度。
2.类似2-3树,但是我们允许每个结点有更多的keys,当结点满的时候,会分解为2个,所以每个结点都在半满和满之间。
3.在B树中插入或查找m阶N个键需要
实际中,M=1024,N=62billion,则最多为4次探索磁盘
4.B树用途:数据库:sql、Oracle、DB2