Data structure basis: the characteristics of red nodes and black nodes in red-black tree, the specific process of querying and inserting data

Red nodes and black nodes in a red-black tree have the following characteristics:

  1. Red node:

    • Red nodes can be root nodes, internal nodes or leaf nodes.
    • The parent node of a red node must be a black node.
    • Both children of the red node are black nodes.
  2. Black node:

    • Black nodes can be root nodes, internal nodes or leaf nodes.
    • The children of a black node can be either red nodes or black nodes.
    • The child nodes of black nodes can be empty nodes (leaf nodes).

The specific process of querying data:

  • Starting from the root node, compare the target value with the key value of the current node.
  • If the target value is less than the key value of the current node, continue searching along the left subtree.
  • If the target value is greater than the key value of the current node, continue searching along the right subtree.
  • If the target value is equal to the key value of the current node, the target node is found and the search is complete.
  • If the target node is not found after reaching the leaf node, it means that the target value does not exist in the tree.

The specific process of inserting data:

  1. Insert the new node into the appropriate position in the red-black tree as a red node.
  2. Depending on the nature of the red-black tree, some tuning may be required to maintain the balance and properties of the red-black tree.
    • If the inserted node is the root node, set its color to black, which satisfies property 1.
    • If the parent node of the inserted node is a black node, it will not violate the nature of the red-black tree, and no additional operations are required.
    • If the parent node of the inserted node is a red node, it needs to be adjusted according to the colors of the parent node and uncle node.
  3. Perform the necessary rotation and color-changing operations to preserve the balance and properties of the red-black tree.
    • If the parent node and uncle node of the inserted node are both red nodes, set the color of the parent node and uncle node to black, set the grandparent node to red, and then make subsequent adjustments based on the grandparent node.
    • If the parent node of the inserted node is a red node, but the uncle node is a black node or an empty node, rotation and color changing operations are required to maintain the balance of the red-black tree.
  4. Check whether the properties of the red-black tree are maintained, if not, continue to adjust until the red-black tree restores balance.

To sum up, when querying data, the red-black tree is traversed step by step according to the comparison of key values ​​until the target node is found or the leaf node is reached. When inserting data, insert the new node into the appropriate position of the red-black tree, and then adjust and repair it according to the nature of the red-black tree to maintain the correctness of balance and nature. These adjustments involve rotation and color-changing operations to ensure that the properties of red-black trees are satisfied.

Java red-black tree implementation

The following is a simple Java code example to implement a red-black tree:

package com.zxl.day_20230603;

enum Color {
    
    
    RED, BLACK
}

class Node {
    
    
    int data;
    Node parent;
    Node left;
    Node right;
    Color color;

    public Node(int data) {
    
    
        this.data = data;
        this.color = Color.RED;
    }
}

public class RedBlackTree {
    
    
    private Node root;
    private Node nil;

    public RedBlackTree() {
    
    
        nil = new Node(0);
        nil.color = Color.BLACK;
        root = nil;
    }

    public void insert(int data) {
    
    
        Node newNode = new Node(data);
        Node current = root;
        Node parent = nil;

        while (current != nil) {
    
    
            parent = current;
            if (data < current.data) {
    
    
                current = current.left;
            } else {
    
    
                current = current.right;
            }
        }

        newNode.parent = parent;
        if (parent == nil) {
    
    
            root = newNode;
        } else if (data < parent.data) {
    
    
            parent.left = newNode;
        } else {
    
    
            parent.right = newNode;
        }

        newNode.left = nil;
        newNode.right = nil;
        newNode.color = Color.RED;
        fixInsert(newNode);
    }

    private void fixInsert(Node node) {
    
    
        while (node.parent.color == Color.RED) {
    
    
            if (node.parent == node.parent.parent.left) {
    
    
                Node uncle = node.parent.parent.right;
                if (uncle.color == Color.RED) {
    
    
                    node.parent.color = Color.BLACK;
                    uncle.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    node = node.parent.parent;
                } else {
    
    
                    if (node == node.parent.right) {
    
    
                        node = node.parent;
                        rotateLeft(node);
                    }
                    node.parent.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    rotateRight(node.parent.parent);
                }
            } else {
    
    
                Node uncle = node.parent.parent.left;
                if (uncle.color == Color.RED) {
    
    
                    node.parent.color = Color.BLACK;
                    uncle.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    node = node.parent.parent;
                } else {
    
    
                    if (node == node.parent.left) {
    
    
                        node = node.parent;
                        rotateRight(node);
                    }
                    node.parent.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    rotateLeft(node.parent.parent);
                }
            }
        }
        root.color = Color.BLACK;
    }

    private void rotateLeft(Node node) {
    
    
        Node rightChild = node.right;
        node.right = rightChild.left;
        if (rightChild.left != nil) {
    
    
            rightChild.left.parent = node;
        }
        rightChild.parent = node.parent;
        if (node.parent == nil) {
    
    
            root = rightChild;
        } else if (node == node.parent.left) {
    
    
            node.parent.left = rightChild;
        } else {
    
    
            node.parent.right = rightChild;
        }
        rightChild.left = node;
        node.parent = rightChild;
    }

    private void rotateRight(Node node) {
    
    
        Node leftChild = node.left;
        node.left = leftChild.right;
        if (leftChild.right != nil) {
    
    
            leftChild.right.parent = node;
        }
        leftChild.parent = node.parent;
        if (node.parent == nil) {
    
    
            root = leftChild;
        } else if (node == node.parent.left) {
    
    
            node.parent.left = leftChild;
        } else {
    
    
            node.parent.right = leftChild;
        }
        leftChild.right = node;
        node.parent = leftChild;
    }

    public void printTree() {
    
    
        printTree(root);
    }

    private void printTree(Node node) {
    
    
        if (node != nil) {
    
    
            printTree(node.left);
            System.out.println(node.data + " " + node.color);
            printTree(node.right);
        }
    }

    public static void main(String[] args) {
    
    
        RedBlackTree tree = new RedBlackTree();
        tree.insert(7);
        tree.insert(3);
        tree.insert(18);
        tree.insert(10);
        tree.insert(22);
        tree.insert(8);
        tree.insert(11);
        tree.insert(26);
        tree.insert(2);
        tree.insert(6);
        tree.insert(13);
        tree.printTree();
    }
}

This example implements the basic functionality of a red-black tree, including inserting and printing the tree. NodeThe class represents a node of a red-black tree, with attributes of data, parent, left child, right child, and color. RedBlackTreeThe class implements the insertion operation and auxiliary methods of the red-black tree.

In insertthe method , a new node is first inserted into the red-black tree and its color is set to red. Then, repair operations are performed according to the properties of the red-black tree, including left rotation, right rotation and color adjustment.

rotateLeftThe and rotateRightmethods implement left-hand and right-hand operations, respectively. These operations are used to keep the red-black tree balanced.

fixInsertmethod is used to repair the properties of red-black trees that may be broken by insert operations. It performs color adjustment and rotation operations according to different situations until the properties of the red-black tree are satisfied.

printTreemethod is used to print the nodes of the red-black tree and their colors. It prints the data and colors of the nodes using the inorder traversal algorithm.

In mainthe method , we create a red-black tree instance and insert some nodes, then print the whole tree.

Note that this is just a simple example of a red-black tree implementation, containing only basic insert operations and printing functionality. In fact, red-black trees can also support operations such as deletion and search, and have more complex properties and rules to follow. A full red-black tree implementation is usually more complicated.

Guess you like

Origin blog.csdn.net/a772304419/article/details/131021601