【手撕红黑树 | 史上最详细注解】增删查改 原理剖析 代码实现

1、基本特征

红黑树起源于2-3-4树(或2-3树),底层是二叉查找树,除二叉树的特性外,还有5大特性:

  • 根是黑色

  • 节点是黑色或红色

  • 叶子节点都是黑色

    叶子节点为NIL节点,不可忽略,见下图

  • 每个红色节点的两个子节点必须是黑色

    从每个叶子到根的路径上不能有两个连续的红节点

  • 黑色平衡

    从任一节点到其所有叶子节点的简单路径上黑色节点数量相同

在这里插入图片描述

2、内部节点类Node

双向链表:父 > 子 子 > 父

    /**
     * 内部节点类
     */
    private class Node {
   
        /**
         * 键 key
         */
        private K key;
        /**
         * 值 value
         */
        private V value;
        /**
         * 父节点
         */
        private Node parent;
        /**
         * 左子节点
         */
        private Node left;
        /**
         * 右子节点
         */
        private Node right;
        /**
         * 节点颜色
         * 红色:true
         * 黑色:false
         */
        private boolean color;

        /**
         * 构造器
         *
         * @param key
         * @param value
         * @param parent
         * @param left
         * @param right
         */
        public Node(K key, V value, Node parent, Node left, Node right) {
   
            this.key = key;
            this.value = value;
            this.parent = parent;
            this.left = left;
            this.right = right;
            this.color = BLACK;
        }
    }

3、成员变量与构造器

/**
 * @author 土味儿
 * Date 2021/9/10
 * @version 1.0
 * 红黑树
 */
public class RedBlackTree1<K extends Comparable<K>, V> {
   
    /**
     * 根节点
     */
    private Node root;
    /**
     * 元素数量
     */
    private int n;
    /**
     * 红色
     */
    private static final boolean RED = true;
    /**
     * 黑色
     */
    private static final boolean BLACK = false;

    /**
     * 构造器
     */
    public RedBlackTree1() {
   
        this.n = 0;
    }
}    

4、基础方法

4.1、元素数量 size()

public

    /**
     * 元素数量
     *
     * @return
     */
    public int size() {
   
        return n;
    }

4.2、节点颜色 colorOf()

private

    /**
     * 节点颜色
     *
     * @param x
     * @return
     */
    private boolean colorOf(Node x) {
   
        if (x != null) {
   
            return x.color == RED;
        }
        return BLACK;
    }

5、查询

5.1、get()

public

    /**
     * 得到 key 结点的值
     *
     * @param key
     * @return
     */
    public V get(K key) {
   
        Node node = getNode(key);
        return node != null ? node.value : null;
    }

5.2、getNode()

private

   /**
     * 获取 key 节点
     *
     * @param key
     * @return
     */
    private Node getNode(K key) {
   
        // 空树、空键
        if (root == null || key == null) {
   
            return null;
        }

        // 从根结点开始查找
        Node node = root;
        // 比较 key 与 h 键的大小
        int cmp;
        // 循环遍历
        while (node != null) {
   
            cmp = key.compareTo(node.key);
            if (cmp < 0) {
   
                node = node.left;
            } else if (cmp > 0) {
   
                node = node.right;
            } else {
   
                return node;
            }
        }
        return null;
    }

5.3、树根

public

    /**
     * 得到树根的value
     * @return
     */
    public V getRoot(){
   
        return root.value;
    }

5.4、最小

  • getMin()

    public

    /**
     * 最小值
     * @return
     */
    public V getMin() {
   
        Node minNode = getMinNode();
        return minNode != null ? minNode.value : null;
    }
  • getMinNode()

    private

    /**
     * 最小节点
     * 最左侧叶子
     *
     * @return
     */
    private Node getMinNode() {
   
        Node p = root;
        if (p != null) {
   
            while (p.left != null) {
   
                p = p.left;
            }
        }
        return p;
    }

5.5、最大

  • getMax()

    public

    /**
     * 最大值
     *
     * @return
     */
    public V getMax() {
   
        Node maxNode = getMaxNode();
        return maxNode != null ? maxNode.value : null;
    }
  • getMaxNode()

    private

    /**
     * 最大节点
     * 最右侧叶子
     *
     * @return
     */
    private Node getMaxNode() {
   
        Node p = root;
        if (p != null) {
   
            while (p.right != null) {
   
                p = p.right;
            }
        }
        return p;
    }

5.6、前驱节点

private

小于当前节点的最大节点

就是当前节点左子树中的最右节点;在左子树中循环向右,最后一个

/**
     * 前驱节点
     * 小于 t 的最大节点
     * t 左子树中最右节点
     *
     * @param t
     * @return
     */
    private Node predecessor(Node t) {
   
        // t 为null时,返回null
        if (t == null) {
   
            return null;
        }

        Node p;
        // t 有左子树:在左子树中找出最右侧节点即可
        if (t.left != null) {
   
            p = t.left;<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值