红黑树及代码实战

红黑树详解及代码实战

历史文章回顾:
平衡二叉树


前言

本文主要讲解红黑树的插入实现,从原理到代码实战

一、红黑树特性

1.任何节点非黑即红
2.根节点是黑色,
3.新节点是红色,叶子空节点是黑色
4.红节点不能相连
5.从任何一个节点出发经历的黑色节点数要一样

由此:红黑树非绝对平衡树,但由3、4点特性,可保证最大高度差不会超过两倍(最短全黑,最长红黑相间)

二、插入节点

新插入得节点因默认红色,所以只可能打破特性4,即父节点是红色
这时有几种可能存在:
1.叔节点是红色,这种好解决,将父和叔变黑,祖变红,再以祖为当前节点继续计算即可
2.叔节点是空或黑色,以当前节点、父、祖三个节点的形状的情况
2.1.子、父、祖在左侧成一条线,将父祖颜色互换(因子是红,父肯定是红,祖肯定是黑),即转换成红黑红的结构,以祖为点右旋
2.2.子、父、祖在右侧成一条线,与上述成镜像,变成左旋即可
2.3.子、父、祖不在一条线,这时先以父为点通过左旋或右旋,调整至一条线,再着色旋转即可

总结:在保证其颜色特性的同时,即已维护了红黑树的平衡性
1.叔节点是红色,将红色上移即可,父叔变黑,祖变红
2.叔是黑(空也是黑),通过旋转调整子、父、祖三点成一条线,由红红黑着色成红黑红,再旋转,以黑为父节点即可

三、代码实战

3.1.RBNode节点

package com.zg.demo.arithmetic.binarytree;

import lombok.Data;

/**
 * RBNode class
 *
 * @author zg
 * @date 2021/4/20 16:41
 */
@Data
public class RBNode {

    private int value;

    private RBNode parentNode;

    private RBNode leftNode;

    private RBNode rightNode;

    private boolean red;

    public RBNode(int value) {
        this.value = value;
        this.red = true;
    }

    public RBNode(int value, boolean red) {
        this.value = value;
        this.red = red;
    }

    public void insert(int value) {
        insert(this, value);
    }

    /**
     * 1.任何节点非黑即红
     * 2.根节点是黑色,
     * 3.新节点是红色,叶子空节点是黑色
     * 4.红节点不能相连
     * 5.从任何一个节点出发经历的黑色节点数要一样
     *
     * 新插入得节点因默认红色,所以只可能打破特性4,即父节点是红色
     * 这时有几种可能存在
     * 1.叔节点是红色,这种好解决,将父和叔变黑,祖变红,再以祖为当前节点继续计算即可
     * 2.叔节点是空或黑色,以当前节点、父、祖三个节点的形状的情况
     *  2.1.子、父、祖在左侧成一条线,将父祖颜色互换(因子是红,父肯定是红,祖肯定是黑),即转换成红黑红的结构,以祖为点右旋
     *  2.2.子、父、祖在右侧成一条线,与上述成镜像,变成左旋即可
     *  2.3.子、父、祖不在一条线,这时先以父为点通过左旋或右旋,调整至一条线,再着色旋转即可
     *
     *  总结下:
     *  1.叔节点是红色,将红色上移即可,父叔变黑,祖变红
     *  2.叔是黑(空也是黑),通过旋转调整子、父、祖三点成一条线,由红红黑着色成红黑红,再旋转,以黑为父节点即可
     * @param root root
     * @param value value
     */
    private void insert(RBNode root, int value) {
        RBNode node = new RBNode(value);
        RBNode parentNode = root;
        // 找到插入节点父节点
        while (true) {
            if (value < parentNode.value) {
                if (parentNode.leftNode != null) {
                    parentNode = parentNode.leftNode;
                    continue;
                }
                parentNode.leftNode = node;
            } else {
                if (parentNode.rightNode != null) {
                    parentNode = parentNode.rightNode;
                    continue;
                }
                parentNode.rightNode = node;
            }
            node.parentNode = parentNode;
            break;
        }
        // 判断父节点颜色,是黑色,则插入结束,是红色需要修复
        if (parentNode.red) {
            insertFixUp(node);
        }
    }

    /**
     * 插入数据修正方法
     * 1.当有且叔叔节点是红色 将父和叔变黑,祖父变红,相当于在黑上加红,下一步对祖父节点判断修正
     * 2.叔叔节点无或者黑色 目标 转换成子 父 祖 左边一条线,将父设为黑,子/祖为红,右旋,下一步对祖父节点判断修正
     * @param node 插入节点
     */
    private void insertFixUp(RBNode node) {
        RBNode parent, grandParent;
        while ((parent = node.parentNode) != null && parent.red) {
            grandParent = parent.parentNode;
            RBNode uncle = getUncleNode(node);
            // 1.叔叔节点是红色 将父节点以及叔叔节点都变为黑色,祖父节点变为红色
            if (uncle != null && uncle.isRed()) {
                parent.red = false;
                uncle.red = false;
                grandParent.red = true;
                // 因祖父节点变红,固需以祖父节点为参考,开始计算修复
                node = grandParent;
                continue;
            }
            // 父节点是左孩子
            if (parent == grandParent.leftNode) {
                // 2.叔叔节点是黑色,当前节点是右孩子,先左旋转换成子 父 祖 一条线
                if (parent.rightNode == node) {
                    leftRotate(parent);
                    RBNode index = parent;
                    parent = node;
                    node = index;
                }
                // 3.是叔叔节点是黑色,且子 父 祖一条线全部在左边
                parent.red = false;
                grandParent.red = true;
                rightRotate(grandParent);
            }
            // 父节点是右孩子,与左孩子成镜像
            if (parent == grandParent.rightNode) {
                if (parent.leftNode == node) {
                    rightRotate(parent);
                    RBNode index = parent;
                    parent = node;
                    node = index;
                }
                parent.red = false;
                grandParent.red = true;
                leftRotate(grandParent);
            }
        }
    }

    private RBNode getUncleNode(RBNode node) {
        RBNode parent = node.parentNode;
        RBNode grandParent = parent.parentNode;
        if (grandParent == null) {
            return null;
        }
        if (parent == grandParent.leftNode) {
            return grandParent.rightNode;
        }
        return grandParent.leftNode;
    }

    /**
     * 右旋
     * 			   5							  3
     *          /    \                         /    \
     * 		   3	  8					     2		 5
     *       /   \                         /      /    \
     * 	   2	 4					     1		4	    8
     *   /
     * 1
     * @param node node
     */
    private void rightRotate(RBNode node) {
        RBNode leftNode = node.leftNode;
        RBNode parentNode = node.parentNode;
        RBNode rightGrandSon = leftNode.rightNode;
        if (parentNode != null) {
            if (parentNode.leftNode == node) {
                parentNode.leftNode = leftNode;
            }
            if (parentNode.rightNode == node) {
                parentNode.rightNode = leftNode;
            }
        }
        leftNode.parentNode = parentNode;
        node.parentNode = leftNode;
        leftNode.rightNode = node;
        node.leftNode = rightGrandSon;
        if (rightGrandSon != null) {
            rightGrandSon.parentNode = node;
        }
    }

    /**
     * 左旋
     *        5                                 7
     *      /   \                           /      \
     *    3      7                         5        8
     *         /   \                      /  \       \
     *        6     8                   3     6       9
     *              \
     *               9
     * @param node node
     */
    private void leftRotate(RBNode node) {
        RBNode rightNode = node.rightNode;
        RBNode parentNode = node.parentNode;
        RBNode leftGrandSon = rightNode.leftNode;
        if (parentNode != null) {
            if (parentNode.leftNode == node) {
                parentNode.leftNode = rightNode;
            }
            if (parentNode.rightNode == node) {
                parentNode.rightNode = rightNode;
            }
        }
        rightNode.parentNode = parentNode;
        node.parentNode = rightNode;
        node.rightNode = leftGrandSon;
        rightNode.leftNode = node;
        if (leftGrandSon != null) {
            leftGrandSon.parentNode = node;
        }
    }

    @Override
    public String toString() {
        return "RBNode{" +
                "value=" + value +
                ", red=" + red +
                '}';
    }
}

3.2.RBTree

package com.zg.demo.arithmetic.binarytree;

import lombok.Data;

/**
 * RBTree class
 *
 * @author zg
 * @date 2021/4/21 10:25
 */
@Data
public class RBTree {

    private RBNode root;

    public void insertNode(int value) {
        if (root == null) {
            root = new RBNode(value, false);
            return;
        }
        root.insert(value);
        setRootNode(root);
        root.setRed(false);
    }

    private void setRootNode(RBNode node) {
        if (node.getParentNode() == null) {
            this.setRoot(node);
            return;
        }
        setRootNode(node.getParentNode());
    }
}

3.3.test

    public static void main(String[] args) {
        //红黑树
        RBTree rbTree = new RBTree();
        for (int i = 0; i < 12; i++) {
            rbTree.insertNode(i);
        }
        printLevel(rbTree.getRoot());
    }
    
	private static void printLevel(RBNode root) {
        LinkedBlockingQueue<RBNode> queue = new LinkedBlockingQueue<>();
        queue.add(root);
        List<List<RBNode>> lists = new ArrayList<>();
        lists.add(new ArrayList<>());
        while (!queue.isEmpty()) {
            RBNode currentNode = queue.poll();
            int level = getLevel(currentNode, 0);
            if (lists.size() == level) {
                lists.add(new ArrayList<>());
            }
            List<RBNode> list1 = lists.get(level);
            list1.add(currentNode);
            if (currentNode.getLeftNode() != null) {
                queue.add(currentNode.getLeftNode());
            }
            if (currentNode.getRightNode() != null) {
                queue.add(currentNode.getRightNode());
            }
        }
        lists.forEach(nodes -> {
            nodes.forEach(node -> {
                System.out.print(node.getValue() + "-" + node.isRed());
                System.out.print("  ");
            });
            System.out.println();
        });
    }

    private static int getLevel(RBNode node, Integer level) {
        if (node.getParentNode() != null) {
            return getLevel(node.getParentNode(), ++level);
        }
        return level;
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值