如何轻松记忆红黑树的插入和删除?

41

理解标准的二叉搜索树及其操作是相当容易的。正因为理解了这一点,我甚至不需要记住插入、删除和搜索操作的实现。

我现在正在学习红黑树,并且理解了它保持平衡的属性。然而,我感觉很难理解它的插入和删除过程。

我知道,在插入新节点时,我们将节点标记为红色(因为红色是我们可以做到的最好的方法,以避免破坏更少的红黑树定律)。新的红色节点可能仍然会破坏“无连续红色节点法则”。然后我们通过以下方式进行修复:

  1. 检查其叔节点的颜色。如果是红色,则将其父节点和叔节点标记为黑色,然后转到祖父节点。

  2. 如果它是右子节点,则左旋其父节点。

  3. 将其父节点标记为黑色,将其祖父节点标记为红色,然后右旋其祖父节点。

完成(基本上如上所述)。

许多地方描述红黑树的插入方式就像以上所述。他们只告诉你如何做。但是为什么这些步骤可以修复树?为什么首先左旋,然后右旋?

有人能够更清楚地解释一下吗,甚至比CLRS更清楚?旋转的魔力是什么?

我真的希望理解,这样在1年后,我就可以自己实现红黑树,而不需要查看书籍。

谢谢


你可以考虑将红黑树转换为相应的2-3 B树表示。 - hugomg
7个回答

20
为了方便阅读本帖的其他读者,如果他们没有接触过被提及在接受答案中的书籍,以下是我希望能够提供一个可接受的描述性答案。
旋转会将树置于满足重新着色的条件下(子节点具有红色叔叔)。这里有两个关键差异:
  • 哪个节点是“子节点”,哪个节点是“叔叔”已经改变;
  • 不再将父节点和叔叔节点重新着为黑色,而是将父节点重新着为红色,祖父节点重新着为黑色。
  • 当子节点没有红色叔叔时,必须进行旋转,因为如果叔节点已经是黑色的,则使父节点变为黑色将仅在祖父节点的一侧增加1个黑高度。这将违反红黑树的高度不变性,并使树失衡。
    现在让我们看看旋转如何转换树,以使我们拥有一个具有红色叔叔的子节点,并可以使用重新着色。我建议您将其绘制出来以充分理解。
    • 设x为当前红色节点,其父节点也是红色。
    • 设p为旋转前x的红色父节点(如果父节点是黑色,那么我们已经完成了)���
    • 设y为旋转前x的黑色叔叔节点(如果叔叔节点是红色,我们将不需要旋转。只需将父节点和叔叔节点重新上色为黑色,祖父节点上色为红色即可)。
    • 设g为旋转前x的黑色祖父节点(因为父节点是红色的,所以祖父节点必须是黑色;否则,这不是一个红黑树)。
    • 当存在左-左(LL)或右-右(RR)情况(即x是p的左子节点且p是g的左子节点,或者x是p的右子节点且p是g的右子节点)时,在进行一次旋转之后(如果是LL情况则向右旋转,如果是RR情况则向左旋转),y成为新的子节点,x成为它的叔叔节点。由于x是红色叔叔,现在可以进行重新上色处理。因此,将子节点的父节点(因为子节点现在是y,其父节点是g)上色为红色,并将子节点的祖父节点(现在是p)上色为黑色。
    • 当存在LR(x是p的左子节点且p是g的右子节点)或RL情况(x是p的右子节点且p是g的左子节点)时,在进行双旋转之后(如果是LR情况则先向右旋转再向左旋转,如果是RL情况则先向左旋转再向右旋转),y成为新的子节点,p成为它的叔叔节点。由于p是红色叔叔,现在可以进行重新上色处理。因此,将子节点的父节点(因为子节点现在是y,其父节点是g)上色为红色,并将子节点的祖父节点(现在是x)上色为黑色。
    在旋转和重新着色之前,您在A面(左侧或右侧)有一个黑色祖先,带有2个红节点和0个黑节点,在B面(与A面相反)有0个红节点和1个黑节点。旋转和重新着色后,您的祖先是黑色的,在A面有1个红节点和0个黑节点,在B面有1个红节点和1个黑节点。因此,您实际上将其中一个红节点移动到祖先的另一个子树中,而不增加任何子树的黑高度。
    这就是旋转的魔力。它允许您将额外的红色节点移动到另一个分支,而不改变黑色高度,并仍然保持二叉搜索树的排序遍历属性。

    1
    你没有提到删除。 - VimNing

    5
    逻辑相当简单。假设z是红色的,而且z的父节点也是红色的: 如果z的叔叔也是红色的,那么就执行步骤1,将有问题的节点向上移动,直到它要么(1)成为根节点。然后只需将根节点标记为黑色。完成。 或者(2)z的叔叔是黑色的。
    在情况(2)下, 要么(a)z是其父节点的左子节点,则步骤3将是最后一步,因为BST的所有属性都得以实现。完成。 要么(b)z是其父节点的右子节点。步骤2将解决问题并转换为情况(a)。然后执行步骤3。完成。
    因此,逻辑是尝试先到达情况(1)和(2a),哪个先到就用哪个解决方法。

    4
    任何2-4(2-3-4)树都可以转换为红黑树。理解2-4树要容易得多。如果您只是通过2-4树中的插入和删除操作,您会感到没有必要记住任何规则就可以实现相同的效果。您将看到一个清晰简单的逻辑,使您能够想出解决不同插入和删除方案的解决方案。
    一旦您对2-4树有了清晰的理解,每当您处理红黑树时,您都可以在心里将这个红黑树映射到2-4树,并自己想出逻辑。
    我发现以下几个视频在理解2-4树、红黑树以及将2-4树映射到红黑树方面非常有用。我建议观看这些视频。
    1)对于2-4树:http://www.youtube.com/watch?v=JZhdUb5F7oY&list=PLBF3763AF2E1C572F&index=13

    2)对于红黑树:http://www.youtube.com/watch?v=JRsN4Oz36QU&list=PLBF3763AF2E1C572F&index=14

    虽然每个视频都长达一小时,但我觉得值得仔细学习。


    3

    请忽略我已被删除的评论——我认为Okasaki的代码会对您有所帮助。如果您有这本书(“纯函数数据结构”),请查看第26页和第3.5图(面对第27页的文本)。这几乎是无法再清晰明了的了。

    不幸的是,可以在线获取的论文没有那一部分内容

    我不会复制它,因为图表很重要,但它表明所有不同情况基本上都是相同的东西,并提供了一些非常简单的ML代码来解释。

    [更新]看起来您可能能够在亚马逊上看到它。转到该书的页面,将鼠标悬停在图像上并在搜索框中输入“红黑色”。这将为您提供包括第25和26页在内的结果,但您需要登录才能查看它们(显然-我没有尝试登录以检查)。


    0

    这是我的实现:

    import java.util.Optional;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    
    /**
     *
     * @author Gaurav Ratnawat
     * <p>
     * Red Black Tree
     * <p>
     * Time complexity
     * Insert - O(logn)
     * Delete - O(logn)
     * Search - O(logn)
     * <p>
     * Does not work for duplicates.
     * <p>
     * References
     * http://pages.cs.wisc.edu/~skrentny/cs367-common/readings/Red-Black-Trees/
     * https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
     */
    public class RedBlackTree {
    
        public enum Color {
            RED,
            BLACK
        }
    
        public static class Node {
            int data;
            Color color;
            Node left;
            Node right;
            Node parent;
            boolean isNullLeaf;
        }
    
        private static Node createBlackNode(int data) {
            Node node = new Node();
            node.data = data;
            node.color = Color.BLACK;
            node.left = createNullLeafNode(node);
            node.right = createNullLeafNode(node);
            return node;
        }
    
        private static Node createNullLeafNode(Node parent) {
            Node leaf = new Node();
            leaf.color = Color.BLACK;
            leaf.isNullLeaf = true;
            leaf.parent = parent;
            return leaf;
        }
    
        private static Node createRedNode(Node parent, int data) {
            Node node = new Node();
            node.data = data;
            node.color = Color.RED;
            node.parent = parent;
            node.left = createNullLeafNode(node);
            node.right = createNullLeafNode(node);
            return node;
        }
    
        /**
         * Main insert method of red black tree.
         */
        public Node insert(Node root, int data) {
            return insert(null, root, data);
        }
    
        /**
         * Main delete method of red black tree.
         */
        public Node delete(Node root, int data) {
            AtomicReference<Node> rootReference = new AtomicReference<>();
            delete(root, data, rootReference);
            if (rootReference.get() == null) {
                return root;
            } else {
                return rootReference.get();
            }
        }
    
        /**
         * Main print method of red black tree.
         */
        public void printRedBlackTree(Node root) {
            printRedBlackTree(root, 0);
        }
    
        /**
         * Main validate method of red black tree.
         */
        public boolean validateRedBlackTree(Node root) {
    
            if (root == null) {
                return true;
            }
            //check if root is black
            if (root.color != Color.BLACK) {
                System.out.print("Root is not black");
                return false;
            }
            //Use of AtomicInteger solely because java does not provide any other mutable int wrapper.
            AtomicInteger blackCount = new AtomicInteger(0);
            //make sure black count is same on all path and there is no red red relationship
            return checkBlackNodesCount(root, blackCount, 0) && noRedRedParentChild(root, Color.BLACK);
        }
    
        private void rightRotate(Node root, boolean changeColor) {
            Node parent = root.parent;
            root.parent = parent.parent;
            if (parent.parent != null) {
                if (parent.parent.right == parent) {
                    parent.parent.right = root;
                } else {
                    parent.parent.left = root;
                }
            }
            Node right = root.right;
            root.right = parent;
            parent.parent = root;
            parent.left = right;
            if (right != null) {
                right.parent = parent;
            }
            if (changeColor) {
                root.color = Color.BLACK;
                parent.color = Color.RED;
            }
        }
    
        private void leftRotate(Node root, boolean changeColor) {
            Node parent = root.parent;
            root.parent = parent.parent;
            if (parent.parent != null) {
                if (parent.parent.right == parent) {
                    parent.parent.right = root;
                } else {
                    parent.parent.left = root;
                }
            }
            Node left = root.left;
            root.left = parent;
            parent.parent = root;
            parent.right = left;
            if (left != null) {
                left.parent = parent;
            }
            if (changeColor) {
                root.color = Color.BLACK;
                parent.color = Color.RED;
            }
        }
    
        private Optional<Node> findSiblingNode(Node root) {
            Node parent = root.parent;
            if (isLeftChild(root)) {
                return Optional.ofNullable(parent.right.isNullLeaf ? null : parent.right);
            } else {
                return Optional.ofNullable(parent.left.isNullLeaf ? null : parent.left);
            }
        }
    
        private boolean isLeftChild(Node root) {
            Node parent = root.parent;
            return parent.left == root;
        }
    
        private Node insert(Node parent, Node root, int data) {
            if (root == null || root.isNullLeaf) {
                //if parent is not null means tree is not empty
                //so create a red leaf node
                if (parent != null) {
                    return createRedNode(parent, data);
                } else { //otherwise create a black root node if tree is empty
                    return createBlackNode(data);
                }
            }
    
            //duplicate insertion is not allowed for this tree.
            if (root.data == data) {
                throw new IllegalArgumentException("Duplicate date " + data);
            }
            //if we go on left side then isLeft will be true
            //if we go on right side then isLeft will be false.
            boolean isLeft;
            if (root.data > data) {
                Node left = insert(root, root.left, data);
                //if left becomes root parent means rotation
                //happened at lower level. So just return left
                //so that nodes at upper level can set their
                //child correctly
                if (left == root.parent) {
                    return left;
                }
                //set the left child returned to be left of root node
                root.left = left;
                //set isLeft to be true
                isLeft = true;
            } else {
                Node right = insert(root, root.right, data);
                //if right becomes root parent means rotation
                //happened at lower level. So just return right
                //so that nodes at upper level can set their
                //child correctly
                if (right == root.parent) {
                    return right;
                }
                //set the right child returned to be right of root node
                root.right = right;
                //set isRight to be true
                isLeft = false;
            }
    
            if (isLeft) {
                //if we went to left side check to see Red-Red conflict
                //between root and its left child
                if (root.color == Color.RED && root.left.color == Color.RED) {
                    //get the sibling of root. It is returning optional means
                    //sibling could be empty
                    Optional<Node> sibling = findSiblingNode(root);
                    //if sibling is empty or of BLACK color
                    if (sibling.isEmpty() || sibling.get().color == Color.BLACK) {
                        //check if root is left child of its parent
                        if (isLeftChild(root)) {
                            //this creates left left situation. So do one right rotate
                            rightRotate(root, true);
                        } else {
                            //this creates left-right situation so do one right rotate followed
                            //by left rotate
    
                            //do right rotation without change in color. So sending false.
                            //when right rotation is done root becomes right child of its left
                            //child. So make root = root.parent because its left child before rotation
                            //is new root of this subtree.
                            rightRotate(root.left, false);
                            //after rotation root should be root's parent
                            root = root.parent;
                            //then do left rotate with change of color
                            leftRotate(root, true);
                        }
    
                    } else {
                        //we have sibling color as RED. So change color of root
                        //and its sibling to Black. And then change color of their
                        //parent to red if their parent is not a root.
                        root.color = Color.BLACK;
                        sibling.get().color = Color.BLACK;
                        //if parent's parent is not null means parent is not root.
                        //so change its color to RED.
                        if (root.parent.parent != null) {
                            root.parent.color = Color.RED;
                        }
                    }
                }
    
            } else {
                //this is mirror case of above. So same comments as above.
                if (root.color == Color.RED && root.right.color == Color.RED) {
                    Optional<Node> sibling = findSiblingNode(root);
                    if (!sibling.isPresent() || sibling.get().color == Color.BLACK) {
                        if (!isLeftChild(root)) {
                            leftRotate(root, true);
                        } else {
                            leftRotate(root.right, false);
                            root = root.parent;
                            rightRotate(root, true);
                        }
                    } else {
                        root.color = Color.BLACK;
                        sibling.get().color = Color.BLACK;
                        if (root.parent.parent != null) {
                            root.parent.color = Color.RED;
                        }
    
                    }
    
                }
    
            }
    
            return root;
        }
    
        /**
         * Using atomicreference because java does not provide mutable wrapper. Its like
         * double pointer in C.
         */
        private void delete(Node root, int data, AtomicReference<Node> rootReference) {
            if (root == null || root.isNullLeaf) {
                return;
            }
            if (root.data == data) {
                //if node to be deleted has 0 or 1 null children then we have
                //deleteOneChild use case as discussed in video.
                if (root.right.isNullLeaf || root.left.isNullLeaf) {
                    deleteOneChild(root, rootReference);
                } else {
                    //otherwise look for the inorder successor in right subtree.
                    //replace inorder successor data at root data.
                    //then delete inorder successor which should have 0 or 1 not null child.
                    Node inorderSuccessor = findSmallest(root.right);
                    root.data = inorderSuccessor.data;
                    delete(root.right, inorderSuccessor.data, rootReference);
                }
            }
            //search for the node to be deleted.
            if (root.data < data) {
                delete(root.right, data, rootReference);
            } else {
                delete(root.left, data, rootReference);
            }
        }
    
        private Node findSmallest(Node root) {
            Node prev = null;
            while (root != null && !root.isNullLeaf) {
                prev = root;
                root = root.left;
            }
            return prev != null ? prev : root;
        }
    
        /**
         * Assumption that node to be deleted has either 0 or 1 non leaf child
         */
        private void deleteOneChild(Node nodeToBeDelete, AtomicReference<Node> rootReference) {
            Node child = nodeToBeDelete.right.isNullLeaf ? nodeToBeDelete.left : nodeToBeDelete.right;
            //replace node with either one not null child if it exists or null child.
            replaceNode(nodeToBeDelete, child, rootReference);
            //if the node to be deleted is BLACK. See if it has one red child.
            if (nodeToBeDelete.color == Color.BLACK) {
                //if it has one red child then change color of that child to be Black.
                if (child.color == Color.RED) {
                    child.color = Color.BLACK;
                } else {
                    //otherwise we have double black situation.
                    deleteCase1(child, rootReference);
                }
            }
        }
    
    
        /**
         * If double black node becomes root then we are done. Turning it into
         * single black node just reduces one black in every path.
         */
        private void deleteCase1(Node doubleBlackNode, AtomicReference<Node> rootReference) {
            if (doubleBlackNode.parent == null) {
                rootReference.set(doubleBlackNode);
                return;
            }
            deleteCase2(doubleBlackNode, rootReference);
        }
    
        /**
         * If sibling is red and parent and sibling's children are black then rotate it
         * so that sibling becomes black. Double black node is still double black so we need
         * further processing.
         */
        private void deleteCase2(Node doubleBlackNode, AtomicReference<Node> rootReference) {
            Node siblingNode = findSiblingNode(doubleBlackNode).get();
            if (siblingNode.color == Color.RED) {
                if (isLeftChild(siblingNode)) {
                    rightRotate(siblingNode, true);
                } else {
                    leftRotate(siblingNode, true);
                }
                if (siblingNode.parent == null) {
                    rootReference.set(siblingNode);
                }
            }
            deleteCase3(doubleBlackNode, rootReference);
        }
    
        /**
         * If sibling, sibling's children and parent are all black then turn sibling into red.
         * This reduces black node for both the paths from parent. Now parent is new double black
         * node which needs further processing by going back to case1.
         */
        private void deleteCase3(Node doubleBlackNode, AtomicReference<Node> rootReference) {
    
            Node siblingNode = findSiblingNode(doubleBlackNode).get();
    
            if (doubleBlackNode.parent.color == Color.BLACK && siblingNode.color == Color.BLACK && siblingNode.left.color == Color.BLACK
                    && siblingNode.right.color == Color.BLACK) {
                siblingNode.color = Color.RED;
                deleteCase1(doubleBlackNode.parent, rootReference);
            } else {
                deleteCase4(doubleBlackNode, rootReference);
            }
        }
    
        /**
         * If sibling color is black, parent color is red and sibling's children color is black then swap color b/w sibling
         * and parent. This increases one black node on double black node path but does not affect black node count on
         * sibling path. We are done if we hit this situation.
         */
        private void deleteCase4(Node doubleBlackNode, AtomicReference<Node> rootReference) {
            Node siblingNode = findSiblingNode(doubleBlackNode).get();
            if (doubleBlackNode.parent.color == Color.RED && siblingNode.color == Color.BLACK && siblingNode.left.color == Color.BLACK
                    && siblingNode.right.color == Color.BLACK) {
                siblingNode.color = Color.RED;
                doubleBlackNode.parent.color = Color.BLACK;
                return;
            } else {
                deleteCase5(doubleBlackNode, rootReference);
            }
        }
    
        /**
         * If sibling is black, double black node is left child of its parent, siblings right child is black
         * and sibling's left child is red then do a right rotation at siblings left child and swap colors.
         * This converts it to delete case6. It will also have a mirror case.
         */
        private void deleteCase5(Node doubleBlackNode, AtomicReference<Node> rootReference) {
            Node siblingNode = findSiblingNode(doubleBlackNode).get();
            if (siblingNode.color == Color.BLACK) {
                if (isLeftChild(doubleBlackNode) && siblingNode.right.color == Color.BLACK && siblingNode.left.color == Color.RED) {
                    rightRotate(siblingNode.left, true);
                } else if (!isLeftChild(doubleBlackNode) && siblingNode.left.color == Color.BLACK && siblingNode.right.color == Color.RED) {
                    leftRotate(siblingNode.right, true);
                }
            }
            deleteCase6(doubleBlackNode, rootReference);
        }
    
        /**
         * If sibling is black, double black node is left child of its parent, sibling left child is black and sibling's right child is
         * red, sibling takes its parent color, parent color becomes black, sibling's right child becomes black and then do
         * left rotation at sibling without any further change in color. This removes double black and we are done. This
         * also has a mirror condition.
         */
        private void deleteCase6(Node doubleBlackNode, AtomicReference<Node> rootReference) {
            Node siblingNode = findSiblingNode(doubleBlackNode).get();
            siblingNode.color = siblingNode.parent.color;
            siblingNode.parent.color = Color.BLACK;
            if (isLeftChild(doubleBlackNode)) {
                siblingNode.right.color = Color.BLACK;
                leftRotate(siblingNode, false);
            } else {
                siblingNode.left.color = Color.BLACK;
                rightRotate(siblingNode, false);
            }
            if (siblingNode.parent == null) {
                rootReference.set(siblingNode);
            }
        }
    
        private void replaceNode(Node root, Node child, AtomicReference<Node> rootReference) {
            child.parent = root.parent;
            if (root.parent == null) {
                rootReference.set(child);
            } else {
                if (isLeftChild(root)) {
                    root.parent.left = child;
                } else {
                    root.parent.right = child;
                }
            }
        }
    
        private void printRedBlackTree(Node root, int space) {
            if (root == null || root.isNullLeaf) {
                return;
            }
            printRedBlackTree(root.right, space + 5);
            for (int i = 0; i < space; i++) {
                System.out.print(" ");
            }
            System.out.println(root.data + " " + (root.color == Color.BLACK ? "B" : "R"));
            printRedBlackTree(root.left, space + 5);
        }
    
        private boolean noRedRedParentChild(Node root, Color parentColor) {
            if (root == null) {
                return true;
            }
            if (root.color == Color.RED && parentColor == Color.RED) {
                return false;
            }
    
            return noRedRedParentChild(root.left, root.color) && noRedRedParentChild(root.right, root.color);
        }
    
        private boolean checkBlackNodesCount(Node root, AtomicInteger blackCount, int currentCount) {
    
            if (root.color == Color.BLACK) {
                currentCount++;
            }
    
            if (root.left == null && root.right == null) {
                if (blackCount.get() == 0) {
                    blackCount.set(currentCount);
                    return true;
                } else {
                    return currentCount == blackCount.get();
                }
            }
            return checkBlackNodesCount(root.left, blackCount, currentCount) && checkBlackNodesCount(root.right, blackCount, currentCount);
        }
    
        public static void main(String[] args) {
            Node root = null;
            RedBlackTree redBlackTree = new RedBlackTree();
    
            root = redBlackTree.insert(root, 10);
            root = redBlackTree.insert(root, 15);
            root = redBlackTree.insert(root, -10);
            root = redBlackTree.insert(root, 20);
            root = redBlackTree.insert(root, 30);
            root = redBlackTree.insert(root, 40);
            root = redBlackTree.insert(root, 50);
            root = redBlackTree.insert(root, -15);
            root = redBlackTree.insert(root, 25);
            root = redBlackTree.insert(root, 17);
            root = redBlackTree.insert(root, 21);
            root = redBlackTree.insert(root, 24);
            root = redBlackTree.insert(root, 28);
            root = redBlackTree.insert(root, 34);
            root = redBlackTree.insert(root, 32);
            root = redBlackTree.insert(root, 26);
            root = redBlackTree.insert(root, 35);
            root = redBlackTree.insert(root, 19);
            redBlackTree.printRedBlackTree(root);
    
            root = redBlackTree.delete(root, 50);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 40);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, -10);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 15);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 17);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 24);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 21);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 32);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 26);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 19);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 25);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 17);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, -15);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 20);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 35);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 34);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 30);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 28);
            System.out.println(redBlackTree.validateRedBlackTree(root));
            root = redBlackTree.delete(root, 10);
            System.out.println(redBlackTree.validateRedBlackTree(root));
        }
    }
    

    0
    也许从 左倾红黑树 开始是值得的。它们提供了一种有趣的简化实现。

    维护树的代码被简化了。因此,您可以将插入和删除修复的代码减少一半。所以没有“如果左边执行这个代码块,如果右边执行这个镜像代码块”的情况。但是没有说的是,为了保持这种左倾不变性,您不可避免地会进行更多的颜色更改和旋转。普通插入最多需要2次旋转,删除最多需要3次旋转。这将增加。所以它并不简单。 - SJHowe
    @SJHowe 不要争论复杂性的形而上学,但通常“简单”的代码并不与其性能相关,而是与我们理解代码/算法的容易程度有关。一些相关概念: https://en.wikipedia.org/wiki/Kolmogorov_complexity https://sep.yimg.com/ty/cdn/paulgraham/bellanguage.txt?t=1595850613& - Peteris

    -2

    你真的很好地总结了这三种情况。

    在RB-Tree中插入后,如果存在两个连续的红色节点,那么就会出现一个主要问题需要解决。 我们如何使这两个连续的红色节点消失而不违反规则(每条路径具有相同数量的黑色节点)? 因此,我们看到这两个节点,只存在3种情况...

    对不起,你可以查看《算法导论》的教材。

    没有人能帮助你理清rb-tree。他们只能在某些关键点上指导你。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接