红黑树的插入操作
1. 以排序二叉树的方法插入新节点,并将它设置为红色。
设置红色:如果设置黑色,会导致根节点到叶子节点的路径上多出一个额外的黑节点,这样会很难调整。
但是设置为红色可能会导致出现两个连续的红色节点(违反了性质4),因此需要通过颜色转换和树旋转来进行调整。
2. 颜色转换和树旋转
新插入的节点N, N的父亲节点设置为P,P的兄弟节点设置为B,P的附近点设置为G。(可以理解为newnode,parent,brother,grandparent)。
情形1:新节点N是树的根节点(root),设置为黑色即可。情形2:新节点N的父节点P为黑色。此时,新插入的节点P为红色,不违反红黑树的各性质。
情形3:父节点P和父节点的兄弟节点B都是红色的。
分析:插入之后破坏了性质4(两个连续的红色节点),所以如图的调整。但是此时新插入节点的父节点的父节点G改变了颜色,那么此时G.color 与 G.parent.color之间的关系就是新的问题所在,所以循环中下一次迭代将G作为新的输入。
这张图相当于在P和N之间做了一个旋转。
情形4:父节点P是红色的,而其兄弟节点B是黑色或者缺少。这里将插入的节点分为左右子节点讨论,其实没有什么区别。
分析:如第二张图,插入的节点是其根结点的右节点,破坏了性质5:一个节点向下到叶子节点的简单路径上的黑色节点数相同。所以进行了一次旋转,状态就和第一张图相似,但是此时破坏了性质4:连续两个红节点,且兄弟节点B为黑色,所以旋转维持性质4.
以上是父节点P作为左子节点,如果是右子节点的情况和这个类似,只是选择的旋转方向不同,这里就不做分析了。
参考代码:
public void add(T data) { // 如果根节点为空 if(root == null){ root = new Node(data, null, null, null); } else{ Node current = root; Node parent = null; int cmp = 0; //搜索到合适的叶子节点,以该节点为父节点添加新的节点 while(current != null){//找到合适的位置 假定的"null"会被我指定的值替换 parent = current; cmp = data.compareTo(current.data); if(cmp < 0){ current = current.left; } else{ current = current.right; } } //创建新节点, 通过直接的比较知道了它的父节点的值, 接下来就是通过比较 判断是他父节点的 左还是右节点 Node newNode = new Node(data, parent, null, null); if(cmp > 0){ parent.right = newNode; } else{ parent.left = newNode; } rbInsert_Fixup(newNode); } } //维护红黑树性质 private void rbInsert_Fixup(Node newNode){ newNode.color = RED; //直到这个新节点的父节点不是根,且newNode的父节点不是红色 while(newNode != null && newNode != root && newNode.parent.color == RED){ //如果newNode的父节点是其父节点的左节点 if(parentOf(newNode) == leftOf(parentOf(parentOf(newNode)))){ //获取newNode的父节点的兄弟节点 Node y = rightOf(parentOf(parentOf(newNode))); //如果它的父节点的兄弟节点是红色 if(colorOf(y) == RED){ //将newNode的父节点设为黑色 setColor(parentOf(newNode), BLACK); //将newNode的父节点的兄弟节点设为黑色 setColor(y, BLACK); //将newNode的父节点的父节点染成红色 setColor(parentOf(parentOf(newNode)), RED); newNode = parentOf(parentOf(newNode)); } //如果newNode的父节点的兄弟节点是黑色 else{ //如果newNode是其父节点的右节点 if(newNode == rightOf(parentOf(newNode))){ //获取newNode的父节点设为newNode newNode = parentOf(newNode); rotateLeft(newNode); } //将newNode的父节点设为黑色 setColor(parentOf(newNode), BLACK); //将newNode的父节点的父节点染成红色 setColor(parentOf(parentOf(newNode)), RED); rotateRight(parentOf(parentOf(newNode))); } } //如果newNode 的父节点是其父节点的右节点 else{ //获取newNode的父节点的兄弟节点 Node y = leftOf(parentOf(parentOf(newNode))); //如果它的父节点的兄弟节点是红色 if(colorOf(y) == RED){ //将newNode的父节点设为黑色 setColor(parentOf(newNode), BLACK); //将newNode的父节点的兄弟节点设为黑色 setColor(y, BLACK); //将newNode的父节点的父节点染成红色 setColor(parentOf(parentOf(newNode)), RED); newNode = parentOf(parentOf(newNode)); } //如果newNode的父节点的兄弟节点是黑色 else{ //如果newNode是其父节点的右节点 if(newNode == leftOf(parentOf(newNode))){ //获取newNode的父节点设为newNode newNode = parentOf(newNode); rotateRight(newNode); } //将newNode的父节点设为黑色 setColor(parentOf(newNode), BLACK); //将newNode的父节点的父节点染成红色 setColor(parentOf(parentOf(newNode)), RED); rotateLeft(parentOf(parentOf(newNode))); } } } //将根节点设为黑色 root.color = BLACK; }
可参考:点击打开链接中对各性质的描述。
以上就是这篇的主要内容。欢迎指出错误和不足之处,谢谢!