红黑树刨析与应用
介绍
红黑树(平衡二叉搜索树)是2-3节点树的一种实现方式
2-3节点树(又称 3阶B树) 是一种完美平衡的树
以 35、75、65、56、78、29、41、37、38 为例
体验中心
https://www.cs.usfca.edu/~galles/visualization/BTree.html
将2-3结点树中的 2-结点 用红色指针连接便转化成了红黑树
红圆圈代表:它与父节点的连接为红连接
此图为自顶向下的红黑树,如果自下向上可以将35,65转成黑连接,在将根连接转成黑色,且38,78结点可以进行左旋转
红黑树的等价定义
• 红连接均为左连接
• 没有任何一个结点同时和两条红色连接相连
• 该树为完美黑色平衡树,即任意空连接到根节点的路径上的黑连接数量相同
结点结构(Node)
private static final boolean RED=true;
private static final boolean BLACK=false;
private class Node{
Key key; //健
Value value; //值
Node left,right; //左右子树
int N; //这颗子树的结点总数
boolean color //结点颜色 RED:true BLACK:false;
}
//工具类
//左旋转
Node rorateLeft(Node h){
Node x=h.right;
h.right=x.left;
x.color=h.color;
h.color=RED;
X.N=h.N;
h.N=1+h.left.N+h.right.N;
return x; //返回值为旋转后父节点的地址即第二张图中的x;
}
(注意看 S,E的位置,粗实线代表红结点,也代表E,S是一个2结点)
| |
\ /
//右选择
Node rorateRight(Node h){
Node.x=h.left;
h.left=x.right;
x.right=h;
x.color=h.color;
h.color=RED;
x.N=h.N;
h.N=1+h.left.N+h.right.N;
return x;//返回值为旋转后父节点的地址即第二张图中的x;
}
| |
\/
旋转的作用
为了满足(不存在两条连续的红连接和不存在红色的右连接)的条件
插入新结点
插入时,要将新结点的链接标注为红链接 (思考2-3结点树的插入动作就知道为什么要这样做了)
①当插入新结点时,将 单个结点转化成2-结点
• 1.当插入的左边时(不用进行任何操作)
•
• 2.当插入的右边时(进行左旋转)
②当插入新结点时,将 2-结点转化成3-结点
• Ⅰ.当插入节点变成下图时(直接将两个红链接变成黑链接并父结点的链接变成红链接即可,红黑树就是以这种方式减少红链接数的)
•
• Ⅱ.当插入节点变成下图时(先进行右旋转在进行Ⅰ操作)
• Ⅲ.当插入节点变成下图时(先进行左旋转在进行Ⅱ操作)
put方法的实现
private isRed(Node node){
if(x==null) return false;
return x.color==RED;
}
①.Ⅰ的情况
private flipColors(Node h){
h.color=RED;
h.left.color=BLACK;
h.right.color=RED;
}
private Node put(Node h,Key key ,Value val){
if(h==null) return New Node(key,val,1,RED); //当走到叶子结点时,创建新结点
int cmp=key.compare(h.key) //如果 key>h.key 返回 1; key=h.key :0 key<h.key -1
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 //有等值的Key就进行替换value即可
if(isRed(h.right)&&!isRed(h.left)) h=rotateLeft(h) ;// ①.2的情况
if(isRed(h.left)&&isRed(h.left.left)) h=rotateRight(h); //②.Ⅱ的情况
if(isRed(h.left)&&isRed(h.right)) flipColors(h);// ②.Ⅰ的情况
h.N=1+h.left.N+h.right.N;
return h;
}
get方法的实现
private Node get(Node h,Key key){
if(h==null) return null
int cmp=key.compare(h.key)
if(cmp<0) return get(h.left,key);
else if(cmp>0) return get(h.right,key);
else return h.val;
}
红黑树的应用
- JDK1.8之后的Hashmap类中当一个桶中Hash碰撞大于8次时,会将链表转化成红黑树,来提高效率
- TreeMap,TreeSet也是用红黑树实现的
HashMap的实现
**
应用的数据结构:数组+链表+红黑树 初始桶数(capctity)为16-----数组实现
负载因子为0.75(loadFactor=size/capctity) ----size:
key-value键值对的数量,当loadFactor>0.75时capctity会进行扩容(扩容的倍数为2的次方)
**
//capctity
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//loadfactor
static final float DEFAULT_LOAD_FACTOR = 0.75f;
关于HashMap的hash实现
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
//将32位的int 分为高16位和低16位进行异或,得到高低16位共同作用的hash值
这么做可以在数组table的length比较小的时候,也能保证高低Bit都参与到Hash的计算中
}
i=(table.length-1)&hash;//进行取模,通过hash确认次key在哪个桶中
//(table.length-1)&hash 等价于 hash%table.length
红黑树,B树,B+树的区别
① 红黑树一个Node只有一个key-value键值对,B树和B+一个Node会有多个Key-value键值对 ②B树的叶子结点不会用链表,B+树的所有key-value键值对都会在叶子结点且用链表链接 这也是为什么
Mysql中的innodb存储引擎有B+树来实现,因为顺序查找更快