插入
向仅有根节点的红黑树插入新节点
如果新键值小于老键值,直接通过红色链接增加一个节点
如果新键值大于老键值,通过红色链接插入节点会产生一条红色右链接,这时候左旋即可
向2-节点插入新节点
2-节点没有与之相连的红链接
这会在红黑树的叶子节点上插入一个新的节点,并且总是用红链接进行插入,因为不增加黑链接,红黑树依然保持黑色完美平衡
这和向仅有根节点的红黑树的插入类似
向3-节点插入新节点
根据新节点的键值,分为三种情况
- 新键最大,通过红色右链接插入新节点,在进行一次颜色转换
- 新键最小,通过红色左链接插入新节点,中间键值的节点上出现了2条红链接,先右旋,再进行颜色转换
- 新键介于两者之间,通过红色右链接插入新节点,中间键值的有2条红链接,先左旋,这时候与上一种情况相同,于是再右旋,最后颜色转换

再实际处理中,因为这三种处理的部分重合,我们将三种基本处理(左旋,右旋,颜色转换)排好顺序,根据情况判断是否进行这一步的处理(三个同级的if)
首先我们通过红链接向一个叶子节点插入新节点,使之称为新节点的父节点,再判断这个父节点的情况进行处理(注意旋转操作是以父节点为参数和接收变量调用的)
- 如果父节点的右子节点是红色,左子节点为黑色,那么左旋
- 如果父节点本身由一个红色链接指向,并且也通过红色链接指向子节点,那么右旋
- 如果父节点的两个链接均为红链接,那么进行颜色转换

到这里,我们就可以写出红黑树的插入方法了
// insert the key-value pair in the subtree rooted at h
private Node put(Node h, Key key, Value val) {
if (h == null) return new Node(key, val, RED, 1);
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;
// fix-up any right-leaning links
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.size = size(h.left) + size(h.right) + 1;
return h;
}
再将这个原始的插入算法与查找,根节点的处理封装一下
public void put(Key key, Value val) {
if (key == null) throw new IllegalArgumentException("first argument to put() is null");
if (val == null) {
delete(key);
return;
}
root = put(root, key, val);
root.color = BLACK;
// assert check();
}