一步一步来实现一下红黑树的删除:红黑树的删除分析
首先,删除方法的入口,进行一定的条件判定:
/** * delValue: delete the value from RBTree * @param value * @return * boolean 返回类型 */ public boolean delValue(T value){ if(this.root == null){ throw new NullIsNotSupported("root is null"); } if(!this.root.isBlack){ throw new UnexpectedException("the tree is not a RBTree"); } if(this.root.value == null || value == null){ return false; } return delValue(this.root,value); }
然后进入第二个方法:根据value值查询到节点t:
/** * delValue: 从以root为根的rb树中删除value * @param root * @param value * @return * boolean 返回类型 */ public boolean delValue(RBNode<T> root,T value){ TreeNode<T> t = this.searchNode(root,value); if(t == null){ return false; } return this.delValue(root, (RBNode<T>)t); }
然后进入第三个方法:找到t节点的后继(真正要删除的节点),更换节点t和后继节点的值:
/** * delValue: 从以root为跟的RB树中删除节点t * @param root * @param t RB节点t * @return * boolean 返回类型 */ public boolean delValue(RBNode<T> root,RBNode<T> t){ RBNode<T> delNode= getSubNode(t); swapValue(delNode,t,false); return this.delNode(delNode); } /** * getSubNode: 找到节点t的后继节点,供删除用 * @param t * @return * RBNode<T> 返回类型 */ private RBNode<T> getSubNode(RBNode<T> t){ int leftDepth = TreeTools.getTreeDepth(t.left); int rightDepth = TreeTools.getTreeDepth(t.right); //如果右子树高,取右子树的最小节点 if(leftDepth < rightDepth){ t = getMinNode(t); }else{ if(t.left.value != null){ t = getMaxNode(t.left); } } return t; } /** * getMinNode: 获取最小节点 * @param t * @return * RBNode<T> 返回类型 */ private RBNode<T> getMinNode(RBNode<T> t){ if(t.left.value != null){ return getMinNode(t.left); } return t; } /** * getMaxNode: 获得最大子节点 * @param t * @return * RBNode<T> 返回类型 */ private RBNode<T> getMaxNode(RBNode<T> t){ if(t.right.value != null){ return getMaxNode(t.right); } return t; }
然后删除真正的节点:
/** * delNode: 删除真正的节点t * @param t * @return * boolean 返回类型 */ public boolean delNode(RBNode<T> t){ //如果t是红色的或t就是根节点,直接删除t即可 if(!t.isBlack || t.equals(this.root)){ t = new RBNode<T>(t.father); return true; }else{ //如果t是黑色的,并且有非空儿子,那么用非空儿子代替他,并且把非空儿子染成黑色即可 if(t.left.value !=null){ t = new RBNode<T>(t.father,t.left.value); t.setBlack(); }else if(t.right.value != null){ t = new RBNode<T>(t.father,t.right.value); t.setBlack(); }else{ //t没有非空儿子,那么删除t,然后调整整棵树 t = new RBNode<T>(t.father); adjust(t); } return true; } }
删除真正节点的时候,如果要删除的是红色节点,或者要删除的节点有红色儿子节点,那么不用进行调整,直接删除或者染相应的颜色即可。其他情况删除完后需要调整:
/** * delNode: 删除真正的节点t * @param t * @return * boolean 返回类型 */ public boolean delNode(RBNode<T> t){ //如果t是红色的或t就是根节点,直接删除t即可 if(!t.isBlack || t.equals(this.root)){ t = new RBNode<T>(t.father); return true; }else{ //如果t是黑色的,并且有非空儿子,那么用非空儿子代替他,并且把非空儿子染成黑色即可 if(t.left.value !=null){ t = new RBNode<T>(t.father,t.left.value); t.setBlack(); }else if(t.right.value != null){ t = new RBNode<T>(t.father,t.right.value); t.setBlack(); }else{ //t没有非空儿子,那么删除t,然后调整整棵树 t = new RBNode<T>(t.father); adjust(t); } return true; } }
调整方法: 后继节点的兄弟是红色的时候:
/** * adjust: 以节点t为调整节点调整树,使它还变成红黑树 * @param t * void 返回类型 */ private void adjust(RBNode<T> t){ //t节点是不是父节点的左孩子 boolean isLeft = t.father.left.equals(t); if(isLeft){ //如果t节点的兄弟是红色的 if(!t.father.right.isBlack){ //逆时针旋转到黑兄弟情况 t = this.rotateWithRightChild(t.father.right).left; } }else{ if(!t.father.left.isBlack){ //顺时针旋转到黑兄弟情况 t = this.rotateWithLeftChild(t.father.left).right; } } this.adjustForBlackBrother(t.right, isLeft); }
当后继节点的兄弟是黑色:
/** * adjustForBlackBrother: 以节点t为调整节点调整树,使它还变成红黑树(t的兄弟节点是黑色的) * @param t * isLeft t节点是否是父节点的左孩子 * void 返回类型 */ private void adjustForBlackBrother(RBNode<T> t,boolean isLeft){ //如果父节点是红色的 if(!t.father.isBlack){ t.father.setBlack(); if(isLeft){ t.father.right.setRed(); }else{ t.father.left.setRed(); } }else{ //首先,在这个条件下,t节点是黑色的,兄弟是黑色的,父节点是黑色的 //如果t节点祖孙三代全黑色(兄弟黑色,兄弟的孩子都黑) if(isLeft && t.father.right.left.isBlack && t.father.right.right.isBlack){ t.father.right.setRed(); adjustForBlackBrother(t.father,isLeft); }else if(!isLeft && t.father.left.left.isBlack && t.father.left.right.isBlack){ t.father.left.setRed(); adjustForBlackBrother(t.father,isLeft); }else{//以下是当t是黑色的,兄弟是黑色的,父节点是黑色的,但是侄子节点有红色节点 //t节点是左子树 if(isLeft){ //t节点的兄弟节点的右孩子是红色的 if(!t.father.right.right.isBlack){ t.father.right.right.setBlack(); this.rotateWithRightChild(t.father.right); }else{ t.father.right.left.setBlack(); this.rotateWithLR(t.father.left); } }else{//t节点是右子树 //t节点的兄弟节点的右孩子是红色的 if(!t.father.left.right.isBlack){ t.father.left.right.setBlack(); this.rotateWithLeftChild(t.father.right); }else{ t.father.left.left.setBlack(); this.rotateWithRL(t.father.left); } } } } }
测试函数:
public static void main(String[] args) { // TODO Auto-generated method stub RBTree<Integer> t = new RBTree<Integer>(1); t.addNode(3); t.addNode(4); t.addNode(5); t.addNode(11); t.addNode(512); t.addNode(4123); t.addNode(7); t.addNode(2); t.addNode(4); System.out.println("中序遍历测试:"); TreeTools.midOrderTravel(t.getRoot()); System.out.println("\n前序遍历测试:"); TreeTools.preOrderTravel(t.getRoot()); System.out.println("\n后序遍历测试:"); TreeTools.backOrderTravel(t.getRoot()); System.out.println("\n层次遍历测试:"); TreeTools.levelTravel(t.getRoot()); System.out.println("\n树的深度:"+TreeTools.getTreeDepth(t.getRoot())); System.out.println("树的叶子个数:"+TreeTools.getLeafNum(t.getRoot())); System.out.println("该树是否是红黑树:"+TreeTools.TreeIsRBTree(t)); System.out.println("查询测试:"+t.searchNode(3)); System.out.println("删除值3-------"); t.delValue(3); System.out.println("查询测试:"+t.searchNode(3)); System.out.println("该树是否是红黑树:"+TreeTools.TreeIsRBTree(t)); }
结果:
增加失败,树里已经有值了 中序遍历测试: 1 2 3 4 5 7 11 512 4123 前序遍历测试: 5 3 1 2 4 512 11 7 4123 后序遍历测试: 2 1 4 3 7 11 4123 512 5 层次遍历测试: 5 3 512 1 4 11 4123 2 7 树的深度:5 树的叶子个数:10 该树是否是红黑树:true 查询测试:true 删除值3------- 查询测试:false 该树是否是红黑树:true