Data structure-6 红黑树(BlackRedTree)插入操作

本文深入探讨红黑树的基本概念及其应用场景,并提供了一个简洁易懂的红黑树Java实现案例,通过三个典型情况解析红黑树的平衡调整策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 红黑树吐槽

在整理之前学过的数据结构知识(主要是应对找工作)时候,动手实现了栈、队列、单链表、双链表、二叉搜索树,上述列举的这些基础的数据结构实现起来还是挺简单的,稍微思考一下即可完成常见的insert/delete/search/traverse操作。

我承认,在红黑树的时候我遇到障碍了,大概花了一天的时间完成了这个版本的红黑树插入操作的代码。后来我反思了一下,之所以这样可能有2个原因:其一,Red Black
Tree确实实现起来相对之前的几种数据结构复杂,这是客观原因,也是最主要的原因。其二,我曾试图参考理解他人博客中关于红黑树实现的代码,包括中文和英文版本的,但要么是那种动辄500-600行的代码,让人望而却步,要么是写的看似很详细,其实完全是bullshit(我不是说别人总结的不对,而是写的不清不楚,浪费别人时间去看,无异于谋财害命。什么?你觉得这篇也是狗屁不通,敬请移步,因为每个人的认知规律是不一样的,我也没办法啊)。

2. 红黑树基础

本文主要思路来源于该网址,这里将其部分内容做简要总结。

One Aim: Approximately balanced tree creation. From root to leaf, no (simple) path is more than twice as long as any other.

Two Rules: (CLRS says five, but others are trivial)
Color Constraint: Red nodes will have both of it’s children black. Black nodes can have either.
Number Constraint: For each node, simple path to all the

Three Situations:
Couple of points to be noted before we see the situations Wherever I
refer Uncle, it means (same as in common life) – “sibling of parent
node”, “the other child of node’s parent’s parent”.
We assume as NIL nodes of all the leaves are black. We always insert red node. So, We are not breaking #2: Number Constraint by adding a black node, but we are breaking constaint #1: Color Constraint. Keep the root node black.
You will see, it makes life easier. It does not breaks any of the
constraints.
1. A Red uncle is a good uncle.
2. A Black uncle on opposite side wants one rotation.
3. A Black uncle on the same side needs two rotations.

  1. 一个目标:红黑树出现的意义:达到几乎平衡树B-树创建的时间复杂度。从root节点到Leaf节点,没有一条路径比其他路径的2倍还长。
  2. 两个原则(共有五个,这里择取重要的2个):
    a). 颜色限制:红色节点的两个孩子必须都是黑色的,而黑色节点则没有这个限制。
    b). 数量限制:对于每个节点,从该节点到所有叶子结点的路径中都有相同数量的黑色节点。
  3. 三种情形:
    a). 叔叔节点为红色的情形:
    这里写图片描述
    b). 叔叔节点为黑色,且和插入节点位于异侧。
    这里写图片描述
    c). 叔叔节点为黑色,且和插入节点位于同侧。
    这里写图片描述

3. 代码实现

//RedBlackTree.java
package com.fqyuan.red_black;

/*
 * A red black tree is a binary search tree which fulfills the 5 policies:
 * 1. Each node is either red or black.
 * 2. The root node is painted black.
 * 3. Each leaf node(nil) is painted black.
 * 4. If a node is red, then both its children is black.
 * 5. For each node, all simple paths from the node to the descent leaves contain the same number of black nodes.
 */
public class RedBlackTree {
    private Node root;

    private enum COLOR {
        RED, BLACK
    };

    private class Node {
        private Node parent;
        private Node lchild;
        private Node rchild;
        private COLOR color;
        private int data;

        public Node(int item) {
            this(item, COLOR.RED);
        }

        public Node(int item, COLOR color) {
            this.data = item;
            this.color = color;
        }
    }

    public void insert(int item) {
        // First, determine if the RBTree is empty.
        if (root == null) {
            Node newNode = new Node(item, COLOR.BLACK);
            root = newNode;
            return;
        }
        Node current = root;
        Node parent = root;
        while (current != null) {
            parent = current;
            if (item < current.data)
                current = current.lchild;
            else
                current = current.rchild;
        }
        // The node 'current' is the position to insert
        Node newNode = new Node(item);
        newNode.parent = parent;

        if (newNode.data < parent.data)
            parent.lchild = newNode;
        else
            parent.rchild = newNode;
        // Do some adjustment.
        fixInsert(newNode);

    }

    private void fixInsert(Node node) {
        // Case 0: If node.parent is Black, or node.parent is null. Then, we are
        // done, wonderful!
        if (node.parent == null || node.parent.color == COLOR.BLACK)
            return;

        // Case 1: Red Uncle is a good uncle. We just need to fix colors.
        Node uncle = getUncle(node);
        if (isUncleRed(uncle)) {
            node.parent.color = COLOR.BLACK;
            node.parent.parent.color = COLOR.RED;
            uncle.color = COLOR.BLACK;
            // Recursive check on the grandparent.
            fixInsert(node.parent.parent);
        } else if (isUncleRightUncle(node)) {
            // Case 3.1: both the node to be inserted and the uncle are on the
            // same side.
            if (node.parent.rchild == node)
                rotateLeft(node); // First rotate to convert to case 2.

            // Case 2.1: the node to be inserted and the black uncle are on
            // opposite side.
            // Fix colors:
            node.parent.color = COLOR.BLACK;
            node.parent.parent.color = COLOR.RED;
            // Fix trees:
            rotateRight(node.parent);
        } else {
            // Case 3.2: both the node to be inserted and the uncle are on the
            // same side.
            if (node.parent.lchild == node)
                rotateRight(node); // First rotate to convert to case 2.

            // Case 2.2: the node to be inserted and the black uncle are on
            // opposite side.
            // Fix colors:
            node.parent.color = COLOR.BLACK;
            node.parent.parent.color = COLOR.RED;
            // Fix trees:
            rotateLeft(node.parent);

        }
        root.color = COLOR.BLACK;

    }

    private Node getUncle(Node node) {
        if (node.parent == null || node.parent.parent == null)
            return null;
        if (node.parent == node.parent.parent.lchild) {
            return node.parent.parent.rchild;
        } else
            return node.parent.parent.lchild;
    }

    private boolean isUncleRed(Node node) {
        return node != null && node.color == COLOR.RED;
    }

    private boolean isUncleRightUncle(Node node) {

        // return node.parent.rchild == node; can not invoke uncle as the
        // parameter for uncle may be null.
        return node.parent == node.parent.parent.lchild;
    }

    private boolean isLeftChild(Node node) {
        return node.parent.lchild == node;
    }

    /**
     * <pre>
     *          g                              g
     *         /                              /
     *       x        Left Rotation(y)       y
     *      /  \     ---------------->      / \
     *  alpha   y                          x   gamma
     *         / \                        / \
     *      beta gamma                alpha  beta
     * </pre>
     * 
     * @param node
     */
    @SuppressWarnings("unused")
    private void rotateLeft(Node node) {
        Node y = node;
        Node x = node.parent;
        Node g = node.parent.parent;

        Node alpha = node.parent.lchild; // Not used.
        Node beta = node.lchild;
        Node gamma = node.rchild; // Not used.

        y.lchild = x;
        y.parent = g;

        x.rchild = beta;
        x.parent = y;
        if (g != null) {
            if (g.lchild == x)
                g.lchild = y;
            else
                g.rchild = y;
        } else {
            root = y;
        }
        if (beta != null)
            beta.parent = x;
    }

    /**
     * <pre>
     *          g                                   g 
     *         /                                   /
     *        y        Right Rotation(x)          x
     *       / \      ---------------->          / \
     *      x   gamma                        alpha  y
     *     / \                                     / \
     * alpha  beta                             beta  gamma
     * </pre>
     */
    @SuppressWarnings("unused")
    private void rotateRight(Node node) {
        Node x = node;
        Node y = node.parent;
        Node g = node.parent.parent;
        Node alpha = node.lchild;
        Node beta = node.rchild;
        Node gamma = y.rchild;

        x.rchild = y;
        x.parent = g;

        y.lchild = beta;
        y.parent = x;

        // Ever made mistake here, attention: the father of y changed because of
        // the code upon.
        if (g != null) {
            if (g.lchild == y)
                g.lchild = x;
            else
                g.rchild = x;
        } else {
            root = x;
        }

        if (beta != null)
            beta.parent = y;
    }

    public boolean search(int item) {
        if (root == null)
            return false;
        Node node = root;

        while (node.data != item) {
            if (item < node.data)
                node = node.lchild;
            else
                node = node.rchild;
            if (node == null)
                return false;
        }
        return true;
    }

    public boolean delete(int item) {
        return false;
    }

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

    private void inOrder(Node node) {
        if (node == null)
            return;
        inOrder(node.lchild);
        // + node.parent != null? node.parent.data : "Root node" + "\n"
        System.out.print(
                node.data + "\t" + node.color + "\t" + ((node.parent == null) ? "Root" : node.parent.data) + "\n");
        inOrder(node.rchild);
    }

    public void deleteRBTree() {

    }
}

//RedBlackDemo.java
package com.fqyuan.red_black;

public class RedBlackDemo {

    public static void main(String[] args) {
        RedBlackTree rbTree = new RedBlackTree();
        int[] arr = { 8, 11, 14, 18, 22, 23, 31, 37, 41, 47, 60, 74, 84, 87, 88, 97, 99 };
        for (int val : arr) {
            rbTree.insert(val);
        }
        rbTree.inOrder();
        if (rbTree.search(4))
            System.out.println("Find 45");
        else
            System.out.println("45 not found.");
    }

}


//Running result:
8   BLACK   11
11  BLACK   18
14  BLACK   11
18  BLACK   Root
22  BLACK   23
23  BLACK   37
31  BLACK   23
37  RED 18
41  BLACK   47
47  RED 74
60  BLACK   47
74  BLACK   37
84  BLACK   87
87  RED 74
88  RED 97
97  BLACK   87
99  RED 97
45 not found.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值