2-3树
即节点可以包含1或2个Node,子节点有2或3个的树。
查找
2-3树的查找采用了迭代的方法,即当不等于当前节点的key时,则迭代进入子节点进行查找,若子节点为空则返回父节点和未找到的标记。若等于当前节点则返回当前节点和找到的标记。
find(node, Key){//也适用于B-tree
if(node==null)
return {isFind = false; node.father};
n = node.getKeyNum();
for(i=1->n){
keyInNode = node.getKey(i);
if(key<keyInNode)
return find(i-1, Key);
else if(Key==KeyInNode)
return {isFind = true; node};
}
if(Key>node.getKey(n))
return find(n+1, Key);
}
插入
2-3树的插入首先是通过查找操作,找到具体的插入位置。然后判断插入的节点是2节点还是3节点。注意,2-3树的插入不会将null节点替换掉,而是改变底层节点,是2节点就变成3节点(该节点的子孩子依然全为null),是3节点就将中间的Key上传到父节点。
红黑树
红黑树其实就是通过将点标记颜色,来将一个3节点转换成由红连接组合的两个2节点。
为避免混乱,总是将红连接设为左孩子的连接。基于2-3树的一系列操作,我们可以设计出黑高度相同的二叉树。
小操作
在涉及插入操作之前, 先要学习几个用于插入操作的小操作。
左旋
对于给定的一棵子树,其根节点为h,则经过左旋之后,其根节点变为x(=h.right)。h变为了x.left。而原先的x.left,则变为了h的h.right。左旋的特点是再不改变结构的情况下,将右子节点的红连接转到了左子节点上。
rotateLeft(node){
x = node.right;
node.right = x.left;
x.left = node;
x.color = h.color;
h.color = RED;
return x;
}
右旋
类似左旋,将子树根节点换成原先根节点的左孩子,并进行相应调整。
颜色转换
将子树根节点的左右子孩子的颜色变黑,子树根节点自身的颜色变红。
插入
为了保证红连接依次向上传递,因此插入操作采用的是递归的方法,即在每次查找过程中的递归操作后再进行一系列的小操作保证红黑树的平衡。并且,默认插入红节点
由于类似2-3树,当我们插入的节点为2节点时,直接插入,体现在红黑树里面就是插入的节点的左右子节点都是黑色的且都为null,因此我们插入的是一个红连接(变为所谓的3节点)。考虑到红黑树的红连接只能是左子节点,因此若插入的key大于node.key,则要进行一次左旋,若小于则不用。
当我们插入的是3节点时,即插入节点的左子节点为红色节点,右子节点为黑色null节点时,分为key小于node.left.key,大于node.left.key小于node.key,大于node.key。三种情况。
- 情况壹,
key>node.key
:插入后则node的左右子节点此时都是红节点,因此要采用颜色转换操作。 - 情况贰,
key<node.left.key
:插入后,会出现两个红连接相连的情况,并且为node的左子节点x为红,x的左子节点为红。因此要先进行右旋操作,将x改为子树的根节点。得到两个红色子节点的x。变成情况壹,最后进行颜色转换操作。 - 情况叁,
node.left.key<key<node.key
:插入后,会出现node的左子节点x为红节点,且x的右子节点为红节点key,因此要先进行左旋操作,将key的红连接换到x的左子节点上。变成情况贰,再对node进行右旋操作,将x改为子树的根节点。得到两个红色子节点的x。变成情况壹,最后进行颜色转换操作。
现在考虑下2节点的两种情况,3节点的三种情况,他们的条件分别是:
- 插入前node的左右子节点为黑
- A:
key>node.key
:node的左子节点为黑,右子节点为红(右旋) - B:
key<node.key
:node的左子节点为红,右子节点为黑(无操作)
- A:
- 插入前node的左子节点为红,右子节点为黑
- C:
node.left.key<key<node.key
:x的左子节点为黑,右子节点为红(右旋(然后进入下一轮递归才会进行后续步骤),左旋,颜色转换) - D:
key<node.left.key
:x的左子节点为红,右子节点为黑(左旋,颜色转换) - E:
key>node.key
:node的左子节点为红,右子节点为红(颜色转换)
- C:
由上可见3节点的处理方式包含了2节点的处理方式,因此再代码中仅用处理3节点情况。
put(node, key, value){
if(node==null)
return new TreeNode(key, value);
//find the node,递归
if(key>node.key)
node.right = put(node.right, key ,value);
else if(key==node.key)
refresh;
else
node.left = put(node.left, key, value);
//调整平衡性
if(左子黑,右子红) rotateLeft(node);
if(左子红,右子黑) rotateRight(node);//注意,此时的子树的根节点指针已经向上移动了一层
if(左子红,右子红) flipColors(node);//颜色转换
//全黑进行下次递归。
return node;
}
删除
删除比较复杂,具体的可以参考:
红黑树详解—彻底搞懂红黑树
在这里主要划分了几种情况:
以上图字母为说明,D点为要删除节点
- 1 要删除的D点没有子节点
- 1.1 D点为红色
- 1.2 D点为黑色
- 1.2.1 兄弟节点B为红色(必有2个黑色子节点,否则D,B高度不一样)
- 1.2.1.1 兄弟节点B有两个黑色子节点
- 1.2.2 兄弟节点B为黑色(若B有子节点,则必为红,否则D,B高度不一样)
- 1.2.2.1 兄弟节点B没有子节点
- 1.2.2.2 兄弟节点B有一个子节点
- 1.2.2.2.1 B的左子节点为红,右子节点为null
- 1.2.2.2.2 B的右子节点为红,左子节点为null
- 1.2.2.3 兄弟节点B有两个子节点
- 1.2.1 兄弟节点B为红色(必有2个黑色子节点,否则D,B高度不一样)
- 2 要删除的D点有一个子节点(D必为黑色,子节点必为红色)
- 3 要删除的D点有两个子节点——利用二叉树的删除方法,转换成1或2类情况。