红黑树
package tree.red.black; /** * @author: tianhaichao * @date: 2022/8/29 14:27 * @description: 1、根节点为黑色 * 2、叶子节点为黑色空节点 * 3、红色节点的孩子节点必须是黑色的 * 4、每个分支上黑色节点个数都一样 * 5、新插入的节点都是红色的(为了减少调整,增加黑色节点会因为打破了【4】而发起调整) */ public class RBTree { private static RBTreeNode root; /** * @author: tianhaichao * @date: 2022/9/20 15:55 * @description: 红黑树插入节点,如果打破了红黑树的规则,需要自平衡做调整 * 分以下几种情况: * 1、父亲节点为【黑色】 ———— 插入红色节点后,没有打破红黑树的规则,【不需要做调整】 * 2、父亲节点为【红色】 ———— 插入节点为红色,需要做【颜色反转】,将父亲节点由红色变为黑色,为了不改变分支上black节点的个数,需要继而向上反转爷爷和祖先节点的颜色 * a、叔叔节点为【红色】 ----- 因为叔叔节点是红色,爷爷节点转变为红色后,叔叔节点必须要转变为黑色。在这个局部的子树中,两个分支上的black节点个数都没有变,所以【不需要左旋或右旋调整】。 * b、叔叔节点为【黑色或空】 ----- 因为叔叔节点是黑色,因为爷爷节点转而为红色,使得叔叔分支上的black节点少了一个,所以需要通过【自平衡】重新回到红黑树 * (叶子结点也是黑色) * RL: 1、执行【颜色反转】2、先以【父节点】为轴【左旋】3、再以【爷爷节点】为轴【右旋】 * RR: 1、执行【颜色反转】2、以【爷爷节点】为轴【右旋 * LR: 1、执行【颜色反转】2、先以【父节点】为轴【右旋】3、再以【爷爷节点】为轴【左旋】 * LL: 1、执行【颜色反转】2、以【爷爷节点】为轴【左旋】 */ public static void insert(int data) { RBTreeNode node = new RBTreeNode(data); if (root == null) { // 根节点是黑色的 root = node; setBlack(node, true); return; } else { // 如果根节点不为空,循环找到叶子节点,执行树插入 RBTreeNode parentNode = root; RBTreeNode sonNode = null; // 比较大小分边 if (data >= parentNode.getData()) { // 遍历右边 sonNode = parentNode.getRight(); } else { // 遍历左边 sonNode = parentNode.getLeft(); } // 子节点不为空,继续遍历,直到找到叶子节点 while (sonNode != null) { parentNode = sonNode; if (data >= parentNode.getData()) { // 遍历右边 sonNode = parentNode.getRight(); } else { // 遍历左边 sonNode = parentNode.getLeft(); } } // sonNode == null 是叶子节点 if (data >= parentNode.getData()) { //插入右节点 parentNode.setRight(node); node.setPrarent(parentNode); // 新建都是红色 setBlack(node, false); } else { //插入左节点 parentNode.setLeft(node); node.setPrarent(parentNode); // 新建都是红色 setBlack(node, false); } // 执行自平衡平衡方法 balanceInsert(node); } } public static void setBlack(RBTreeNode node, boolean isBlack) { if(node == null){ return; } if (node != root) { node.setBlack(isBlack); } else { node.setBlack(true); } } /** * @author: tianhaichao * @date: 2022/9/20 17:38 * @description:自平衡分以下几种情况: 1、父亲节点为【黑色】 ———— 插入红色节点后,没有打破红黑树的规则,【不需要做调整】 * 2、父亲节点为【红色】 ———— 插入节点为红色,需要做【颜色反转】,将父亲节点由红色变为黑色,为了不改变分支上black节点的个数,需要继而向上反转爷爷和祖先节点的颜色 * a、叔叔节点为【红色】 ----- 因为叔叔节点是红色,爷爷节点转变为红色后,叔叔节点必须要转变为黑色。在这个局部的子树中,两个分支上的black节点个数都没有变,所以【不需要左旋或右旋调整】。 * b、叔叔节点为【黑色或空】 ----- 因为叔叔节点是黑色,因为爷爷节点转而为红色,使得叔叔分支上的black节点少了一个,所以需要通过【自平衡】重新回到红黑树 * (叶子结点也是黑色) * RL: 1、执行【颜色反转】2、先以【父节点】为轴【左旋】3、再以【爷爷节点】为轴【右旋】 * RR: 1、执行【颜色反转】2、以【爷爷节点】为轴【右旋 * LR: 1、执行【颜色反转】2、先以【父节点】为轴【右旋】3、再以【爷爷节点】为轴【左旋】 * LL: 1、执行【颜色反转】2、以【爷爷节点】为轴【左旋】 */ public static void balanceInsert(RBTreeNode node) { //判断father的颜色 RBTreeNode farther = node.getPrarent(); // 父节点是红色的,需要自平衡 while (farther != root && farther != null && farther.isBlack() == false) { RBTreeNode grandFarther = farther.getPrarent(); // 父亲节点是祖父的右孩子 if (grandFarther.getRight() == farther) { // 判断叔叔节点颜色 —— red 【颜色反转】不需要【左旋】或【右旋】调整 // 如果叔叔节点不为空,且为红色,反转叔叔节点颜色,祖父节点作为父节点向上传递 if (grandFarther.getLeft() != null && grandFarther.getLeft().isBlack() == false) { setBlack(farther, true); setBlack(grandFarther, false); setBlack(grandFarther.getLeft(), true); // 此时的改变已经传递到了祖父,祖父变成了红色,相当于新插入 farther = grandFarther.getPrarent(); node= grandFarther; continue; } else { //叔叔节点为黑色或空 if(farther.getLeft()==node){ //RL 右旋 成为RR 然后 以祖父节点左旋 rightRotate(farther); } //RR farther = leftRotate(grandFarther); setBlack(farther, true); setBlack(farther.getRight(), false); setBlack(farther.getLeft(), false); // 通过调整,father替代祖父的位置变成了黑色,所有分支黑色节点的个数没有变化,整棵树重新满足红黑树规则,跳出循环 break; } } // 父亲节点是祖父的左孩子 if (grandFarther.getLeft() == farther) { // 判断叔叔节点颜色 —— red 【颜色反转】不需要【左旋】或【右旋】调整 // 如果叔叔节点不为空,且为红色,反转叔叔节点颜色,祖父节点作为父节点向上传递 if (grandFarther.getRight() != null && grandFarther.getRight().isBlack() == false) { setBlack(farther, true); setBlack(grandFarther, false); setBlack(grandFarther.getRight(), true); // 此时的改变已经传递到了祖父,祖父变成了红色,相当于新插入 farther = grandFarther.getPrarent(); node= grandFarther; continue; } else { //叔叔节点为黑色或空 if(farther.getRight()==node){ //LR 左旋 成为LL 然后 以祖父节点右旋 farther = leftRotate(farther); } //LL farther = rightRotate(grandFarther); setBlack(farther, true); setBlack(farther.getRight(), false); setBlack(farther.getLeft(), false); } } } } /** * @author: tianhaichao * @date: 2022/8/30 14:44 * @description: node 旋转节点 RR 、LR执行左旋 * 1、将右节点上提成为父节点 * 2、父节点转为右节点的左孩子 * 3、父节点的右孩子【替换成】右节点的左孩子,父节点的左孩子不变 * return 旋转完后的父节点,也就是右孩子【将右节点上提成为父节点】 */ public static RBTreeNode leftRotate(RBTreeNode node) { RBTreeNode right = node.getRight(); RBTreeNode father = node; // 如果右节点存在左孩子,父节点的右孩子【替换成】右节点的左孩子 father.setRight(right.getLeft()); // 将右节点上提成为父节点 变成祖父的孩子 if (father.getPrarent() == null || father == root) { root = right; } else if (father.getPrarent().getLeft() == father) { father.getPrarent().setLeft(right); } else { // 如果旋转节点是父节点的右节点 father.getPrarent().setRight(right); } right.setPrarent(father.getPrarent()); // 父节点转为右节点的左孩子 right.setLeft(father); father.setPrarent(right); return right; } /** * @return 旋转之后的父节点,也就是左孩子【将左节点上提成为父节点】 * @author: tianhaichao * @date: 2022/9/20 18:31 * @description: LL或RL执行右旋 * 执行右旋, * 1、将左节点上提成为父节点 * 2、父节点转为左节点的右孩子 * 3、父节点的左孩子【替换成】左节点的右孩子,父节点的右孩子不变 */ public static RBTreeNode rightRotate(RBTreeNode node) { RBTreeNode left = node.getLeft(); RBTreeNode father = node; // 父节点的左孩子【替换成】左节点的右孩子,父节点的右孩子不变 father.setLeft(left.getRight()); //将左节点上提成为父节点 if (father.getPrarent() == null || father == root) { root = left; } else if (father.getPrarent().getRight() == father) { father.getPrarent().setRight(left); } else { father.getPrarent().setLeft(left); } left.setPrarent(father.getPrarent()); // 父节点转为左节点的右孩子 father.setPrarent(left); left.setRight(father); return left; } public static void main(String[] args) { int[] array = new int[]{10, 3, 5, 2, 5, 6, 4, 12, 15, 16, 17, 18}; for (int i : array) { RBTree.insert(i); preTraversal(root); System.out.println("----" + i); } System.out.println("=========================="); //排序输出 midTraversal(root); } /** * @author: tianhaichao * @date: 2022/9/20 15:10 * @description:中序遍历 */ public static void midTraversal(RBTreeNode node) { if (node == null) { return; } midTraversal(node.getLeft()); System.out.print(node.getData() +" "); midTraversal(node.getRight()); } /** * @author: tianhaichao * @date: 2022/9/20 15:10 * @description:中序遍历 */ public static void preTraversal(RBTreeNode node) { if (node == null) { return; } int left = node.getLeft()!=null?node.getLeft().getData():0; int right = node.getRight() != null?node.getRight().getData():0; System.out.print(node.getData() +"【"+node.isBlack()+left+" "+right+"】"+ " "); preTraversal(node.getLeft()); preTraversal(node.getRight()); } } package tree.red.black; /** * @author: tianhaichao * @date: 2022/8/29 14:20 * @description: 红黑树节点 */ public class RBTreeNode { private int data; private RBTreeNode prarent; private RBTreeNode left; private RBTreeNode right; // get set方法省略…… }
验证工具:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html