红黑二叉树
基本思想:用标准的二叉树和一些额外信息来表示2-3树。
红链接将2-结点连接起来构成一个3-结点,黑链接则是2-3树中普通链接。
红黑树的自身特性:
1、结点的颜色一定是红色或黑色
2、树根的颜色为黑色
3、叶子结点为黑色(叶子结点为外部结点,是空结点)
4、某红结点,其左右孩子结点一定为黑结点
5、任一结点从该节点出发到其叶子结点的路径上均包含相同数目的黑色结点
(其他说法
1、红链接均是左链接
2、没有任何一个结点同时和两条红链接相连
3、该树是完美黑色平衡的,即任意链接到根结点的路径上的黑色链接数量相同。
)
黑高度:bh(x)表示。即从x出发(不包含x)到其叶子结点的黑色结点的个数。
具有n个内部结点的红黑树,树高最多为2lg(n+1)
1、结点的定义:
每个结点都有一条指向自己的链接(从它的父节点指向它),将链接的颜色保存在表示结点的Node数据类型的布尔值color之中。如果为红色就是True,黑色为false。空链接为黑色。
结点的定义
1 public class RBT<Key extends Comparable<Key>,Value> { 2 3 private static final boolean RED =true; 4 private static final boolean BLACK = false; 5 6 private class Node{ 7 Key key; 8 Value val; 9 Node left,right; 10 int N; 11 boolean color; 12 13 Node(Key key,Value val,int N,boolean color){ 14 this.key=key; 15 this.val=val; 16 this.N=N; 17 this.color=color; 18 } 19 } 20 21 private boolean isRed(Node x){ 22 if(x==null) 23 return false; 24 return x.color==RED; 25 } 26 27 }
2、旋转
主要是解决的红色的右链接和两条连续的红链接。
对于红色的右链接的解决方案是左旋。
实现方式:将两个键中较小的为根变为较大的为根,之后返回变换之后的根结点
代码:
1 Node rotateLeft(Node h){ 2 Node x=h.right; 3 h.right=x.left; 4 x.left=h; 5 x.color=h.color; 6 h.color=RED; 7 x.N=h.N; //没有变,因为是根 8 h.N=1+size(h.left)+size(h.right); 9 return x; 10 }
两条连续的红链接的解决方案是右旋
实现方式:将两个键中较大的为根变为较小的为根,之后返回变换之后的根结点
1 Node rotateRight(Node h){ 2 Node x= h.left; 3 h.left=x.right; 4 x.right=h; 5 x.color=h.color; 6 h.color=RED; 7 x.N=h.N; 8 h.N=1+size(h.left)+size(h.right); 9 return x; 10 }
3、向单个2-结点中插入新键
插入另外一个键之后,需要马上将他们旋转。
1、如果新键小于老键,只需新增一个红色结点即可
2、新键大于老键,那么新增的红色结点将会产生一条红色的右键,需要rotateLeft(Node h)旋转。
4、向树底部的2-结点插入新键
与3类似
5、向一颗双键中插入新键
5.1新键大于原树中的两个键,他被连接到3-结点的右链接。此时的树是平衡的,根节点为中间大小的键,他有两条红键分别与较小和较大的结点相连,此时只需把两条链接的颜色都由红变黑即可。
5.2新建小于原树中的两个键,他会被链接到左边的空链接,这是就产生了连续的红链接,此时只需将上层的红链接右旋即可得到5.1的情况。
5.3新键介于原树中的两个键之间,这是又会产生两条连续的红连接,一条红色左链接,一条红色右链接,之后左旋得5.2,在右旋得5.1
6、颜色转换
转换一个结点的两个红色子结点的颜色,还要把父节点的颜色由黑变红
void flipColors(Node h){ h.color=RED; h.left.color=BLACK; h.right.color=BLACK; }
7、根总是黑色
8、向树的底部的3-结点插入新键
会出现前面的三种情况。颜色转变之后使得中间的链接变红,相当于将他送入了父节点,意味着在父节点中继续出入了一个新键。
9、将红链接在树中向上传递
红黑树中实现2-3树的插入算法的关键操作所需的步骤:要在一个3-结点下插入新键,先创建一个临时的4-结点,将其分解并将其红链接有中间键传递给他的父节点。重复这个工程,直至遇到一个2-节点或根节点。
总之,使用左旋转、右旋转和颜色转换,就能实现。在沿着插入点到根节点的路劲向上移动时在所经过的每个结点中顺序完成以下步骤:
1、如果右子节点是红色的二左结点是黑色的,就进行左旋转
2、如果 左子结点是红色的且它的左子结点也是红色的,就进行右旋转
3、如果左右子结点都是红色,进行颜色转换
插入的最终实现:
public void put(Key key,Value val){ 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; }
对于插入操作
分成了两个步骤:1、安找二叉树方式插入 2、恢复红黑树的特性
为什么选择红结点插入:
1、当选择黑结点的时候,一定破坏特性5
2、如果选择红结点时,特性2、4有可能被破坏
二者者相害,取其轻。
其他人的讲解: