最近学习了二叉搜索树中的红黑树,感觉收获颇丰,在此写一篇文章小结一下学到的知识,顺便手写一下Java代码。
// 颜色枚举 enum RBColor { RED, BLACK; } // 树节点类 class RBTreeNode { RBTreeNode p = nullNode; // 父节点 RBTreeNode left = nullNode; // 左子节点 RBTreeNode right = nullNode; // 右子节点 int val; // 值 RBColor color; // 颜色 public RBTreeNode() {}; RBTreeNode(int val) { this.val = val; } @Override public String toString() { return " (" + val + " " + color + ") "; } // 用于表示空叶节点的静态变量 public static RBTreeNode nullNode = new RBTreeNode() { { color = RBColor.BLACK; // 叶结点为黑色 } @Override public String toString() { return " (null " + color + ") "; } }; }
/** * 左旋操作 * @param root 根结点引用 * @param node 旋转的节点 * @return 根节点 */ public static RBTreeNode leftRotate(RBTreeNode root, RBTreeNode node) { if (node.right == RBTreeNode.nullNode) return root; // 左旋需要拥有右节点 RBTreeNode right = node.right; // 旋转节点的右子树变为右节点的左子树 node.right = right.left; if (node.right != RBTreeNode.nullNode) node.right.p = node; // 用右节点代替旋转节点位置 if (node.p != RBTreeNode.nullNode) { right.p = node.p; if (node.p.left == node) node.p.left = right; else node.p.right = right; } else { root = right; // 没有父节点的节点为根结点 root.p = RBTreeNode.nullNode; } // 右节点的左子树变为旋转节点 right.left = node; node.p = right; return root; } /** * 右旋操作 * @param root 根结点引用 * @param node 旋转节点 * @return 根节点 */ public static RBTreeNode rightRotate(RBTreeNode root, RBTreeNode node) { if (node.left == RBTreeNode.nullNode) return root; // 右旋需要有左节点 RBTreeNode left = node.left; // 旋转节点的左子树变为左节点的右子树 node.left = left.right; if (node.left != RBTreeNode.nullNode) node.left.p = node; // 用左节点代替旋转节点 if (node.p != RBTreeNode.nullNode) { left.p = node.p; if (node.p.left == node) node.p.left = left; else node.p.right = left; } else { root = left; root.p = RBTreeNode.nullNode; } // 左节点的右子树变为旋转节点 left.right = node; node.p = left; return root; }
/** * 红黑树插入操作 * @param root 根结点引用 * @param insertNode 要插入的新节点 * @return 根节点 */ public static RBTreeNode rbInsert(RBTreeNode root, RBTreeNode insertNode) { RBTreeNode position = root, parent = RBTreeNode.nullNode; // position为插入位置,parent为该位置的父节点 while (position != RBTreeNode.nullNode) { parent = position; if (insertNode.val < position.val) // 比该节点元素小的节点应该插入其左子树 position = position.left; else // 比该节点元素大的节点应该插入其右子树 position = position.right; } insertNode.p = parent; if (parent == RBTreeNode.nullNode) // 没有父节点的节点为根结点 root = insertNode; else if (insertNode.val < parent.val) // 插入为左节点 parent.left = insertNode; else // 插入为右节点 parent.right = insertNode; insertNode.color = RBColor.RED; // 把新插入的节点染成红色 return rbInsertFixup(root, insertNode); // 修复插入时红黑树性质 }
/** * 修復插入時违反的红黑树性质 * @param root 根节点引用 * @param node 修复节点 * @return 根节点 */ public static RBTreeNode rbInsertFixup(RBTreeNode root, RBTreeNode node) { // 修复节点不是根节点且为红色时 RBTreeNode parent = node.p, grandParent, parentBorther; while(parent != RBTreeNode.nullNode && parent.color == RBColor.RED) { grandParent = parent.p; if (grandParent.left == parent) { // 父节点为左节点 parentBorther = grandParent.right; // 叔节点为右节点 if (parentBorther != RBTreeNode.nullNode && parentBorther.color == RBColor.RED) { // case 1 grandParent.color = RBColor.RED; // 祖父节点改为红色 parent.color = RBColor.BLACK; // 父节点和叔节点改为黑色 parentBorther.color = RBColor.BLACK; node = grandParent; // 对祖父节点继续遍历 } else { if (parent.right == node) { // case 2 root = leftRotate(root, parent); // 对父节点左旋 // 交换node和parent的引用 RBTreeNode temp = node; node = parent; parent = temp; } // case 3 grandParent.color = RBColor.RED; // 祖父染成红色 parent.color = RBColor.BLACK; // 父节点染成黑色 root = rightRotate(root, grandParent); // 对祖父右旋 node = root; // 把节点置为根节点退出修复 } } else { // 父节点为右节点,镜像处理 parentBorther = grandParent.left; if (parentBorther != RBTreeNode.nullNode && parentBorther.color == RBColor.RED) { // case 1 grandParent.color = RBColor.RED; parent.color = RBColor.BLACK; parentBorther.color = RBColor.BLACK; node = grandParent; } else { if (parent.left == node) { // case 2 root = rightRotate(root, parent); RBTreeNode temp = node; node = parent; parent = temp; } // case 3 grandParent.color = RBColor.RED; parent.color = RBColor.BLACK; root = leftRotate(root, grandParent); node = root; } } parent = node.p; } // 根节点染为黑色 root.color = RBColor.BLACK; return root; }
/** * 红黑树删除操作 * @param root 根节点引用 * @param deleteNode 要删除的节点 * @return 根节点 */ public static RBTreeNode rbDelete(RBTreeNode root, RBTreeNode deleteNode) { RBTreeNode replaceNode, fixNode = RBTreeNode.nullNode; // 顶替删除节点的代替节点、需要修复颜色的节点位置 RBTreeNode fixNodeParent = deleteNode.p; RBColor deleteColor = deleteNode.color; // 记录被删除节点的颜色 if (deleteNode.left == RBTreeNode.nullNode && deleteNode.right == RBTreeNode.nullNode) // 删除节点没有任何子结点 replaceNode = RBTreeNode.nullNode; else if (deleteNode.right == RBTreeNode.nullNode) { // 处理只有左子节点的情况 replaceNode = deleteNode.left; fixNode = replaceNode; } else if (deleteNode.left == RBTreeNode.nullNode) { //处理只有右子节点的情况 replaceNode = deleteNode.right; fixNode = replaceNode; } else { // 处理有两个子节点的情况 replaceNode = deleteNode.right; while (replaceNode.left != RBTreeNode.nullNode) // 找到右子树的最小节点 replaceNode = replaceNode.left; fixNode = replaceNode.right; // 修复节点位置变为原顶替节点位置 if (replaceNode.p == deleteNode) { // 特殊情况,右子树没有左节点 if (fixNode != RBTreeNode.nullNode) // 修复节点不为空 fixNode.p = replaceNode; fixNodeParent = replaceNode; } else { replaceNode.p.left = fixNode; // 修复节点顶替该节点的位置 if (fixNode != RBTreeNode.nullNode) // 修复节点不为空 fixNode.p = replaceNode.p; fixNodeParent = replaceNode.p; replaceNode.right = deleteNode.right; } // 用删除节点的颜色代替顶替节点的颜色,使得被删除颜色的节点实际变为顶替节点 deleteColor = replaceNode.color; replaceNode.color = deleteNode.color; replaceNode.left = deleteNode.left; } if (replaceNode != RBTreeNode.nullNode) // 存在顶替节点 replaceNode.p = deleteNode.p; if (deleteNode.p == RBTreeNode.nullNode) // 删除节点的父节点为空,是根节点 root = replaceNode; else { // 删除节点不是根节点 if (deleteNode.p.left == deleteNode) deleteNode.p.left = replaceNode; else deleteNode.p.right = replaceNode; } if (deleteColor == RBColor.BLACK) // 如果删除的颜色是黑色则需要进行修复 root = rbDeleteFixup(root, fixNode, fixNodeParent); return root; }
/** * 修复删除时破坏的红黑树性质 * @param root 根引用 * @param fixNode 修复位置 * @param parent 修复位置的父节点(修复位置为叶结点时使用) * @return 根 */ public static RBTreeNode rbDeleteFixup(RBTreeNode root, RBTreeNode fixNode, RBTreeNode parent) { RBTreeNode brother; while (root != fixNode && fixNode.color == RBColor.BLACK) { parent = fixNode.p == null ? parent : fixNode.p; // 处理fixNode为nullNode情况 if (fixNode == parent.left) { // 顶替位置在父节点左边 brother = parent.right; if (brother.color == RBColor.RED) { // case 1 // 交换父节点和兄弟节点的颜色 RBColor temp = brother.color; brother.color = parent.color; parent.color = temp; // 父节点进行左旋 root = leftRotate(root, parent); } else if (brother == RBTreeNode.nullNode) { // case 2 // 兄弟节点为空,即为黑色,只需继续遍历父节点即可 fixNode = parent; } else if (brother.left.color == RBColor.BLACK && brother.right.color == RBColor.BLACK) { // case 2 brother.color = RBColor.RED; fixNode = parent; // 继续遍历父节点 } else { // case 3 and case 4 if (brother.left.color == RBColor.RED && brother.right.color == RBColor.BLACK) { // case 3 // 兄弟节点染成红色,左子节点染成黑色 brother.color = RBColor.RED; brother.left.color = RBColor.BLACK; // 兄弟节点右旋 root = rightRotate(root, brother); brother = brother.p; } // case 4 // 变色 brother.color = parent.color; parent.color = RBColor.BLACK; brother.right.color = RBColor.BLACK; // 父节点左旋 root = leftRotate(root, parent); break; } } else { brother = parent.left; if (brother.color == RBColor.RED) { // case 1 // 交换父节点和兄弟节点的颜色 RBColor temp = brother.color; brother.color = parent.color; parent.color = temp; // 父节点进行右旋 root = rightRotate(root, parent); } else if (brother == RBTreeNode.nullNode) { // case 2 // 兄弟节点为空,即为黑色,只需继续遍历父节点即可 fixNode = parent; } else if (brother.left.color == RBColor.BLACK && brother.right.color == RBColor.BLACK) { // case 2 brother.color = RBColor.RED; fixNode = parent; // 继续遍历父节点 } else { // case 3 and case 4 if (brother.right.color == RBColor.RED && brother.left.color == RBColor.BLACK) { // case 3 // 兄弟节点染成红色,左子节点染成黑色 brother.color = RBColor.RED; brother.right.color = RBColor.BLACK; // 兄弟节点右旋 root = leftRotate(root, brother); brother = brother.p; } // case 4 // 变色 brother.color = parent.color; parent.color = RBColor.BLACK; brother.left.color = RBColor.BLACK; // 父节点左旋 root = rightRotate(root, parent); break; } } } fixNode.color = RBColor.BLACK; return root; };
public static void main(String[] args) { int num[] = new int[]{5, 4, 1, 6, 3, 2}; List<RBTreeNode> list = new ArrayList<>(); RBTreeNode root = RBTreeNode.nullNode; // 插入测试 for (int i = 0; i < num.length; i++) { list.add(new RBTreeNode(num[i])); root = rbInsert(root, list.get(i)); printRBTree(root); System.out.println(""); } // 删除测试 for (int i = 0; i < num.length; i++) { root = rbDelete(root, list.get(0)); list.remove(0); printRBTree(root); System.out.println(""); } } /** * 打印一颗红黑树 * @param root 根节点的引用 */ public static void printRBTree(RBTreeNode root) { if (root == RBTreeNode.nullNode) { System.out.println("这是一颗空树"); return; } Queue<RBTreeNode> q = new LinkedList<>(); boolean allNull = false; // 是否全为空节点 q.add(root); while (!allNull) { // 该行不是全为叶结点 allNull = true; Queue<RBTreeNode> rowQ = new LinkedList<>(); // 用于存储一行的所有节点 RBTreeNode node; while (!q.isEmpty()) { node = q.poll(); System.out.print(node); if (node != RBTreeNode.nullNode) { // 该节点不是叶结点 if (node.left != RBTreeNode.nullNode) { rowQ.add(node.left); allNull = false; } else rowQ.add(RBTreeNode.nullNode); if (node.right != RBTreeNode.nullNode) { rowQ.add(node.right); allNull = false; } else rowQ.add(RBTreeNode.nullNode); } else { // 该节点为叶节点 rowQ.add(RBTreeNode.nullNode); rowQ.add(RBTreeNode.nullNode); } } q = rowQ; System.out.println(""); } }