在学习红黑树的时候,看了很多文章,发现都没有讲明白红黑树的原理,只是简单列了红黑树的几条规则,就开始讲解红黑树的插入,让人一直不知其所以然。也很难深刻的理解红黑树。
最后翻起了《算法》这本书,仔细了解了二叉树查找树、2-3树、红黑树,才明白了红黑树不是平白无故产生的,而是符合科学的发展观念是循序渐进,站在巨人的肩旁上发展起来的。
这也是我们从学生时代的填鸭式的学习方式的转变,不仅仅是只学习结论,并且要了解结论产生的历史及发展。
这样我们才能从搬砖、码农成长为工程师、科学家。
一位计算机科学的大牛说过:
程序 = 数据结构 + 算法
数据结构是为了减少查询、删除的时间复杂度和空间复杂度。
链表的一个节点是由:节点值、节点的下一个节点(字节点)的地址
二叉查找树一个节点:节点值、节点的左子节点的地址、节点的右子节点的地址
2-3树:由2节点、3节点组成
红黑树(平衡二叉查找树):节点值、节点的左子节点、节点的右子节点、节点颜色
二叉查找树:是由2节点的树组成的,最坏的时间复杂度是O(N)
插入的几种情况:
根据以上的四种情况,我们可以看出,如果我们插入的值是从1开始逐渐递增的,那么树就最终生长得很像链表。
private Node<T> root = null;//根节点
private class Node<T>{
public T value;//为了下面的简单演示设属性为 pubilc
public Node<T> left;//真正使用的时候应该为private 用get、set方法访问
public Node<T> right;
public Node<T> parent;
public Node(T value, Node<T> parent) {
this.value = value;
this.parent = parent;
}
}
如果查找的值等于根节点的值,那么查找命中,否则递归的在根的子树中(通过比较与根节点的值选择左/右节点)查找。
//返回null 树中没有此节点 ,递归的下左/右子节点查询
public Node<T> get(T value){
Node<T> node = root;
while(true){
if(node == null){
return null;
}
if(node.value == value){
return node;
}else if( value > node.value){
if(node.right == null){
return null;
}else if(node.right.value == value){
return node.right;
}
node = node.right;
}else if( value < node.value ){
if(node.left == null){
return null;
}else if(node.left.value == value){
return node.left;
}
node = node.left;
}
}
}
插入和查找很像,如果树的根节点为空(空树)则插入到根节点,否则递归的在子树中判断,当子树为null的时候,插入。
public Node<T> put(T value){
Node<T> node = root;
while(true){
if(node == null){
Node<T> node1 = new Node<>(value,null);
root = node1;//空树,插入的为根节点
return node1;
}
if(node.value == value){
return node;
}else if(node.value > value){
if(node.right == null){
Node<T> node1 = new Node<>(value,node);
node.right = node1;
return node1;
}else if(node.right.value == value){
return node.right;
}
node = node.right;
}else if(node.value < value){
if(node.left == null){
Node<T> node1 = new Node<>(value,node);
node.left = node1;
return node1;
}else if(node.left.value == value){
return node.left;
}
node = node.left;
}
}
}
为了解决二叉查找树的不平衡,2-3树孕育而生,2-3树能够很好的实现树的平衡。
2-3树的节点不再是单一的2节点,节点可能是2节点、3节点。
2节点:有一个值,两个子节点
3节点:有两个值,三个子节点。
注意二叉查找树是向下生长,而2-3树是向上生长。
当2-3的根节点由3树生长为(红黑树中的旋转)2节点,树的高度增加。
2-3树的查找和二叉查找树的思路是相同的都是迭代的思想。不过不同的是在判断节点是否相同时2-3树要判断是否相等于左值或右值,如果大于左值小于右值,则从中子节点开始递归。
2-3树的插入有以下几种情况:
插入的节点为2节点,2节点转化为3节点。
插入的节点为3节点,则临时调整3节点为4节点(三个值),4节点中的中值变为左右值的父。
则3节点的中子节点转为父节点的值,父节点临时转化为4节点,此时就变成了插入的节点为3节点的情况上面的两种情况。
通过对比,2-3和二叉树的上图的插入,可以看到2-3最终是会自平衡的,而二叉查找树最坏会变成链表的形式(长的像链表).
红黑树其实是2-3树的一种只含2节点的表现形式。还是二叉树节点大于左子节点,小于右子节点。
我们把2-3树中的2节点用黑色表示,3节点用红色表示(3节点的左节点为黑色、右节点为红色)
将红色链接画平就是2-3树。
左旋、右旋其实就是2-3树的3节点临时变为4节点,4节点的分解。
把红黑树的插入看成是2-3树的插入,就能明白红黑树的节点插入,节点的旋转,及根据红黑树的
对比2-3的几种插入情况,就能理解红黑树的插入情况。
https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/5-TreeSet and TreeMap.md
一个红黑树生成过程: https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
张无忌的回答:
https://www.zhihu.com/question/22774822/answer/46376239
建议看一下《算法》第四版红皮书。
其实理解红黑树起来也不是那么困难。