文章首发于:clawhub.club
红黑树插入操作与二叉查找树的插入方式是一样的,之不过插入后,会影响到树的平衡,所以要对树进行旋转和着色操作,使其符合红黑树的5条性质。
继续贴上性质:
- 每个结点要么是红的,要么是黑的。
- 根结点是黑的。
- 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
- 如果一个结点是红的,那么它的俩个儿子都是黑的。
- 对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。
新插入的节点是红色的(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。),
插入修复操作如果遇到父节点的颜色为黑则修复操作结束。也就是说,只有在父节点为红色节点的时候是需要插入修复操作的。
为什么要插入修复?
因为插入节点后,会影响到性质4与5。
不会造成性质破坏的插入
1插入节点作为根节点
直接插入后,将节点设置为黑色。
2插入节点作为根节点的子节点
既然能作为根节点的字节点,即数中只有根节点与叶子节点为黑色。不会影响到性质4与5。
3插入节点的父结点是黑色的
因为插入的节点使红色的,所以不会影响到性质4与5。
即只有祖父节点不为空并且父结点是红色的,才需要插入修复。
需要修复的插入
插入修复情况1:
当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色。
修复操作为:将叔、父重绘为黑色,祖绘为红色,这种修复并不是一次性的,修复完毕需要继续进行从头开始的插入后修复判断。
插入修复情况2:
叔结点为黑色(空),并且子父祖三节点位于同侧斜线上。
修复操作为:将父绘黑,祖绘红,然后以祖为枢纽进行向相反侧的方向旋转,同在右侧斜线上的就往左旋转,同在左侧斜线上的就往右旋转。
插入修复情况3:
叔结点为黑色(空),并且子父祖三节点位于异侧斜线上。
修复操作分为了两步:
第一步是根据子父所在的那一侧的方向,以父为枢向相反方向旋转,这样就实现了将子父祖异侧转换为了父子祖同侧(并且是子父原所在侧的相反侧);
第二步是发现它符合 插入修复情况2 了,那就将现在的父(原来的子)绘黑,祖(原来的祖)绘红,再旋转。
插入操作很复杂,学的我脑壳疼。以下准备用JAVA代码实现上述操作。
辅助函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
private Node<T> parentOf(Node<T> p) { return (p == null ? null : p.parent); }
private boolean isRed(Node<T> p) { return p.color == Node.RED; }
void makeRed(Node<T> p) { p.color = Node.RED; }
void makeBlack(Node<T> p) { p.color = Node.BLACK; }
|
二叉搜索树插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
private Node<T> insertBST(T value) { Node<T> current = new Node<>(value); Node<T> parent = null;
Node<T> temp = root; while (true) { if (temp == null) { break; } parent = temp;
if (temp.value.equals(current.value)) { current.freq++; return temp;
} else if (temp.value.compareTo(current.value) < 0) { temp = temp.right; } else { temp = temp.left; } } if (null != parent) { if (parent.value.compareTo(current.value) < 0) { current.parent = parent; parent.right = current; } else { current.parent = parent; parent.left = current; } } else { root = current; } return current; }
|
红黑树插入修正(重点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
private void insertFixUp(Node<T> node) { Node<T> parent, gparent;
while (((parent = parentOf(node)) != null) && isRed(parent)) { gparent = parentOf(parent); Node<T> uncle; if (parent == gparent.left) { uncle = gparent.right; } else { uncle = gparent.left; }
if ((uncle != null) && isRed(uncle)) { makeBlack(uncle); makeBlack(parent); makeRed(gparent); node = gparent; continue; }
if (parent == gparent.left) { if (parent.right == node) { Node<T> tmp; rotateLeft(parent); tmp = parent; parent = node; node = tmp; } if (parent.left == node) { makeBlack(parent); makeRed(gparent); rotateRight(gparent); }
} else { if (parent.left == node) { Node<T> tmp; rotateRight(parent); tmp = parent; parent = node; node = tmp; } if (parent.right == node) { makeBlack(parent); makeRed(gparent); rotateLeft(gparent); }
} } makeBlack(root); }
|
红黑树插入操作
1 2 3 4 5 6 7 8 9 10 11 12
|
public void insert(T value) { Node<T> current = insertBST(value); insertFixUp(current); }
|
参考文档:
红黑树深入剖析及Java实现
教你透彻了解红黑树
红黑树(五)之 Java的实现