【无标题】

什么是红黑树?

红黑树是一种特殊的二叉查找树,它的每个节点都有一个颜色属性(红色或黑色)。这种数据结构由两位计算机科学家Leland和Tarjan于1978年发明,主要用于提高二叉查找树的操作效率。

红黑树的性质

  1. 节点颜色: 每个节点要么是红色,要么是黑色。
  2. 根节点是黑的: 根节点总是涂成黑色。
  3. 叶节点是黑的:所有的叶子节点(NIL节点,空节点)都是黑色的。
  4. 红色节点的子节点必须是黑色的: 如果一个节点是红色的,那么它的两个子节点都是黑色的。
  5. 从任一节点到其后代的所有路径包含相同数目的黑节点: 这保证了树的左右两边保持平衡。

红黑树的操作

插入

插入操作遵循以下步骤:

按照二叉查找树的方式插入新节点,并将新节点着色为红色。
如果新插入的节点破坏了红黑树的性质,则通过旋转和重新着色将其修复。

package org.zyf.javabasic.rbtest;
 
/**
 * @program: zyfboot-javabasic
 * @description:
 * @author: zhangyanfeng
 * @create: 2023-08-19 13:10
 **/
public class RedBlackTreeDelete {
 
    private TreeNode root;
 
    enum Color {
        RED, BLACK
    }
 
    class TreeNode {
        int key;
        Color color;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
 
        public TreeNode(int key, Color color) {
            this.key = key;
            this.color = color;
            this.left = null;
            this.right = null;
            this.parent = null;
        }
    }
 
    // 省略其他方法...
 
    public void delete(int key) {
        TreeNode nodeToDelete = findNodeToDelete(root, key);
        if (nodeToDelete != null) {
            deleteNode(nodeToDelete);
        }
    }
 
    private void deleteNode(TreeNode nodeToDelete) {
        TreeNode replacementNode = (nodeToDelete.left != null && nodeToDelete.right != null)
                ? findSuccessor(nodeToDelete.right)
                : nodeToDelete;
 
        TreeNode childNode = (replacementNode.left != null) ? replacementNode.left : replacementNode.right;
 
        if (childNode != null) {
            childNode.parent = replacementNode.parent;
        }
 
        if (replacementNode.parent == null) {
            root = childNode;
        } else if (replacementNode == replacementNode.parent.left) {
            replacementNode.parent.left = childNode;
        } else {
            replacementNode.parent.right = childNode;
        }
 
        if (replacementNode != nodeToDelete) {
            nodeToDelete.key = replacementNode.key; // 将替代节点的值赋给被删除节点
        }
 
        if (replacementNode.color == Color.BLACK) {
            deleteFixup(childNode, replacementNode.parent); // 进行颜色调整和可能的旋转操作
        }
    }
 
    // 寻找要删除的节点
    private TreeNode findNodeToDelete(TreeNode current, int key) {
        if (current == null || current.key == key) {
            return current;
        }
        if (key < current.key) {
            return findNodeToDelete(current.left, key);
        }
        return findNodeToDelete(current.right, key);
    }
 
    // 寻找后继节点
    private TreeNode findSuccessor(TreeNode node) {
        TreeNode current = node;
        while (current.left != null) {
            current = current.left;
        }
        return current;
    }
 
    private void deleteFixup(TreeNode x, TreeNode xParent) {
        while (x != root && (x == null || x.color == Color.BLACK)) {
            if (x == xParent.left) {
                TreeNode sibling = xParent.right;
 
                if (sibling.color == Color.RED) {
                    // 情况 1:兄弟节点为红色
                    sibling.color = Color.BLACK;
                    xParent.color = Color.RED;
                    leftRotate(xParent);
                    sibling = xParent.right;
                }
 
                if ((sibling.left == null || sibling.left.color == Color.BLACK) &&
                        (sibling.right == null || sibling.right.color == Color.BLACK)) {
                    // 情况 2:兄弟节点和其子节点都为黑色
                    sibling.color = Color.RED;
                    x = xParent;
                    xParent = xParent.parent;
                } else {
                    if (sibling.right == null || sibling.right.color == Color.BLACK) {
                        // 情况 3:兄弟节点是黑色,且兄弟节点的左子节点是红色
                        if (sibling.left != null) {
                            sibling.left.color = Color.BLACK;
                        }
                        sibling.color = Color.RED;
                        rightRotate(sibling);
                        sibling = xParent.right;
                    }
 
                    // 情况 4:兄弟节点是黑色,且兄弟节点的右子节点是红色
                    sibling.color = xParent.color;
                    xParent.color = Color.BLACK;
                    if (sibling.right != null) {
                        sibling.right.color = Color.BLACK;
                    }
                    leftRotate(xParent);
                    x = root; // 完成调整,退出循环
                }
            } else {
                // 同上,但是左右对调
            }
        }
 
        if (x != null) {
            x.color = Color.BLACK; // 将 x 染黑
        }
    }
 
    private void leftRotate(TreeNode x) {
        TreeNode y = x.right;
        x.right = y.left;
        if (y.left != null) {
            y.left.parent = x;
        }
        y.parent = x.parent;
        if (x.parent == null) {
            root = y;
        } else if (x == x.parent.left) {
            x.parent.left = y;
        } else {
            x.parent.right = y;
        }
        y.left = x;
        x.parent = y;
    }
 
    private void rightRotate(TreeNode y) {
        TreeNode x = y.left;
        y.left = x.right;
        if (x.right != null) {
            x.right.parent = y;
        }
        x.parent = y.parent;
        if (y.parent == null) {
            root = x;
        } else if (y == y.parent.left) {
            y.parent.left = x;
        } else {
            y.parent.right = x;
        }
        x.right = y;
        y.parent = x;
    }
}

删除

删除操作稍微复杂一些,因为需要处理被删除节点的三种情况:

被删除的节点是叶子节点(没有子节点)。
被删除的节点有一个子节点。
被删除的节点有两个子节点。
删除后,可能需要进行一系列的旋转和重新着色来维持红黑树的性质。

package org.zyf.javabasic.rbtest;
 
/**
 * @program: zyfboot-javabasic
 * @description:
 * @author: zhangyanfeng
 * @create: 2023-08-19 13:10
 **/
public class RedBlackTreeDelete {
 
    private TreeNode root;
 
    enum Color {
        RED, BLACK
    }
 
    class TreeNode {
        int key;
        Color color;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
 
        public TreeNode(int key, Color color) {
            this.key = key;
            this.color = color;
            this.left = null;
            this.right = null;
            this.parent = null;
        }
    }
 
    // 省略其他方法...
 
    public void delete(int key) {
        TreeNode nodeToDelete = findNodeToDelete(root, key);
        if (nodeToDelete != null) {
            deleteNode(nodeToDelete);
        }
    }
 
    private void deleteNode(TreeNode nodeToDelete) {
        TreeNode replacementNode = (nodeToDelete.left != null && nodeToDelete.right != null)
                ? findSuccessor(nodeToDelete.right)
                : nodeToDelete;
 
        TreeNode childNode = (replacementNode.left != null) ? replacementNode.left : replacementNode.right;
 
        if (childNode != null) {
            childNode.parent = replacementNode.parent;
        }
 
        if (replacementNode.parent == null) {
            root = childNode;
        } else if (replacementNode == replacementNode.parent.left) {
            replacementNode.parent.left = childNode;
        } else {
            replacementNode.parent.right = childNode;
        }
 
        if (replacementNode != nodeToDelete) {
            nodeToDelete.key = replacementNode.key; // 将替代节点的值赋给被删除节点
        }
 
        if (replacementNode.color == Color.BLACK) {
            deleteFixup(childNode, replacementNode.parent); // 进行颜色调整和可能的旋转操作
        }
    }
 
    // 寻找要删除的节点
    private TreeNode findNodeToDelete(TreeNode current, int key) {
        if (current == null || current.key == key) {
            return current;
        }
        if (key < current.key) {
            return findNodeToDelete(current.left, key);
        }
        return findNodeToDelete(current.right, key);
    }
 
    // 寻找后继节点
    private TreeNode findSuccessor(TreeNode node) {
        TreeNode current = node;
        while (current.left != null) {
            current = current.left;
        }
        return current;
    }
 
    private void deleteFixup(TreeNode x, TreeNode xParent) {
        while (x != root && (x == null || x.color == Color.BLACK)) {
            if (x == xParent.left) {
                TreeNode sibling = xParent.right;
 
                if (sibling.color == Color.RED) {
                    // 情况 1:兄弟节点为红色
                    sibling.color = Color.BLACK;
                    xParent.color = Color.RED;
                    leftRotate(xParent);
                    sibling = xParent.right;
                }
 
                if ((sibling.left == null || sibling.left.color == Color.BLACK) &&
                        (sibling.right == null || sibling.right.color == Color.BLACK)) {
                    // 情况 2:兄弟节点和其子节点都为黑色
                    sibling.color = Color.RED;
                    x = xParent;
                    xParent = xParent.parent;
                } else {
                    if (sibling.right == null || sibling.right.color == Color.BLACK) {
                        // 情况 3:兄弟节点是黑色,且兄弟节点的左子节点是红色
                        if (sibling.left != null) {
                            sibling.left.color = Color.BLACK;
                        }
                        sibling.color = Color.RED;
                        rightRotate(sibling);
                        sibling = xParent.right;
                    }
 
                    // 情况 4:兄弟节点是黑色,且兄弟节点的右子节点是红色
                    sibling.color = xParent.color;
                    xParent.color = Color.BLACK;
                    if (sibling.right != null) {
                        sibling.right.color = Color.BLACK;
                    }
                    leftRotate(xParent);
                    x = root; // 完成调整,退出循环
                }
            } else {
                // 同上,但是左右对调
            }
        }
 
        if (x != null) {
            x.color = Color.BLACK; // 将 x 染黑
        }
    }
 
    private void leftRotate(TreeNode x) {
        TreeNode y = x.right;
        x.right = y.left;
        if (y.left != null) {
            y.left.parent = x;
        }
        y.parent = x.parent;
        if (x.parent == null) {
            root = y;
        } else if (x == x.parent.left) {
            x.parent.left = y;
        } else {
            x.parent.right = y;
        }
        y.left = x;
        x.parent = y;
    }
 
    private void rightRotate(TreeNode y) {
        TreeNode x = y.left;
        y.left = x.right;
        if (x.right != null) {
            x.right.parent = y;
        }
        x.parent = y.parent;
        if (y.parent == null) {
            root = x;
        } else if (y == y.parent.left) {
            y.parent.left = x;
        } else {
            y.parent.right = x;
        }
        x.right = y;
        y.parent = x;
    }
}

旋转

红黑树中的左旋和右旋是用于维护树的平衡性的旋转操作。

左旋

当需要解决一个结点因为其子节点过大而违反红黑树的性质时,可以通过左旋来调整。左旋通常发生在当前节点是红色,它的叔叔节点(即父节点的兄弟节点)是黑色,并且当前节点位于其父节点的右侧时。在左旋操作中,会将当前节点的父节点作为旋转的中心,使得当前节点上升为其祖父节点的左孩子,同时原父节点变为当前节点的右孩子。

右旋

与左旋相对,右旋是在当前节点位于其父节点的左侧时进行的旋转操作。右旋的目的是让当前节点上升为其祖父节点的右孩子,同时原父节点变为当前节点的左孩子。

package org.zyf.javabasic.rbtest;
 
/**
 * @program: zyfboot-javabasic
 * @description:
 * @author: zhangyanfeng
 * @create: 2023-08-19 12:38
 **/
public class RedBlackTree {
 
    private TreeNode root;
 
    public RedBlackTree() {
        root = null;
    }
 
    // 左旋操作
    private void leftRotate(TreeNode x) {
        TreeNode y = x.right; // y 为 x 的右子节点
        x.right = y.left; // 将 y 的左子节点设置为 x 的右子节点
        if (y.left != null) {
            y.left.parent = x;
        }
        y.parent = x.parent; // 更新 y 的父节点
        if (x.parent == null) {
            root = y;
        } else if (x == x.parent.left) {
            x.parent.left = y;
        } else {
            x.parent.right = y;
        }
        y.left = x; // 将 x 设为 y 的左子节点
        x.parent = y;
    }
 
    // 右旋操作
    private void rightRotate(TreeNode y) {
        TreeNode x = y.left; // x 为 y 的左子节点
        y.left = x.right; // 将 x 的右子节点设置为 y 的左子节点
        if (x.right != null) {
            x.right.parent = y;
        }
        x.parent = y.parent; // 更新 x 的父节点
        if (y.parent == null) {
            root = x;
        } else if (y == y.parent.left) {
            y.parent.left = x;
        } else {
            y.parent.right = x;
        }
        x.right = y; // 将 y 设为 x 的右子节点
        y.parent = x;
    }
 
}

应用场景

红黑树广泛应用于各种需要快速插入、删除和查找操作的场景,例如:

  1. 数据库索引
  2. 关联数组
  3. 动态集合
  4. 有序映射。
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值