Java实现红黑树并绘制红黑树

步骤一:实现红黑树

定义红黑树节点类

package rbtree;


import java.util.List;

public class RBTreeNode<T> {
    private T val;//值
    private boolean red;//是否为红
    private RBTreeNode<T> parentNode;//父节点
    private RBTreeNode<T> leftNode;//左节点
    private RBTreeNode<T> rightNode;//右节点
    public RBTreeNode() {
    }

    public RBTreeNode(T val, boolean red) {
        this.val = val;
        this.red = red;
    }


    public RBTreeNode(T val, boolean red, RBTreeNode<T> parentNode) {
        this.val = val;
        this.red = red;
        this.parentNode = parentNode;
    }

    public RBTreeNode(T val, boolean red, RBTreeNode<T> parentNode, RBTreeNode<T> leftNode, RBTreeNode<T> rightNode) {
        this.val = val;
        this.red = red;
        this.parentNode = parentNode;
        this.leftNode = leftNode;
        this.rightNode = rightNode;
    }

    public RBTreeNode<T> getParentNode() {
        return parentNode;
    }

    public void setParentNode(RBTreeNode<T> parentNode) {
        this.parentNode = parentNode;
    }

    public RBTreeNode<T> getLeftNode() {
        return leftNode;
    }

    public void setLeftNode(RBTreeNode<T> leftNode) {
        this.leftNode = leftNode;
    }

    public RBTreeNode<T> getRightNode() {
        return rightNode;
    }

    public void setRightNode(RBTreeNode<T> rightNode) {
        this.rightNode = rightNode;
    }

    public boolean isRed() {
        return red;
    }

    public void setRed(boolean red) {
        this.red = red;
    }

    public T getVal() {
        return val;
    }

    public void setVal(T val) {
        this.val = val;
    }

    /**
     * 中序遍历 左根右
     */
    public void middleIterator() {
        if(leftNode != null) {
            leftNode.middleIterator();
        }
        System.out.println(String.format("%s_%s",val,isRed() ? "R" :"B"));
        if(rightNode != null) {
            rightNode.middleIterator();
        }
    }

    /**
     * 中序遍历 并且计算出所有节点的位置坐标和连线
     * @param circularList 
     * @param lineList
     * @param coordinate
     * @return
     */
    public Coordinate middleIterator(List<Circular> circularList, List<Line> lineList, Coordinate coordinate) {
        int curY = coordinate.getCurY();
        if(leftNode != null) {
            coordinate.setCurY(curY + 1);
            coordinate = leftNode.middleIterator(circularList, lineList,coordinate);
            //当前节点-左节点
            lineList.add(new Line(coordinate.getCurX(),curY,coordinate.getNextX(),curY+1));
        }
        circularList.add(new Circular(coordinate.getCurX(),curY,String.valueOf(val),red));
        int curX = coordinate.getCurX();
        coordinate.setCurX(curX + 1);
        if(rightNode != null) {
            coordinate.setCurY(curY + 1);
            coordinate = rightNode.middleIterator(circularList, lineList,coordinate);
            //当前节点-右节点
            lineList.add(new Line(curX,curY,coordinate.getNextX(),curY+1));

        }
        coordinate.setNextX(curX);
        return coordinate;
    }


}

逻辑实现红黑树

package rbtree;


import java.util.List;


public class RBTree<T> {

    private RBTreeNode<T> root;

    /**
     * 添加节点
     * @param t
     */
    public void addNode(T t){
        //插入元素
        RBTreeNode insertNode = insertNode(t);
        if(insertNode == root){
            return;
        }
        //旋转或变色处理
        rotateOrChangeColor(insertNode);

    }

    /**
     * 旋转或变色处理
     * @param insertNode
     */
    private void rotateOrChangeColor(RBTreeNode insertNode){
        RBTreeNode<T> parentNode = insertNode.getParentNode();//父节点
        RBTreeNode<T> uncleNode = null;//叔父节点
        RBTreeNode<T> grandParentNode = null;//祖父节点
        //2.变色旋转
        while (parentNode != null){
            if(!parentNode.isRed()){
                //2.1.插入节点的父节点为黑色节点,则无需做任何操作
                return;
            }
            //获取祖父节点与叔父节点
            grandParentNode = parentNode.getParentNode();
            if(grandParentNode.getLeftNode() == parentNode){
                uncleNode = grandParentNode.getRightNode();
            }else{
                uncleNode = grandParentNode.getLeftNode();
            }
            if(uncleNode != null && uncleNode.isRed()){
                //2.2.父节点与叔父节点同色 变色处理即可
                //1) 父节点和叔父节点变为黑色
                parentNode.setRed(false);
                uncleNode.setRed(false);
                //2)祖父节点为根节点则无需做任何操作
                if(grandParentNode == root){
                    return;
                }
                //3)祖父节点设置为红色
                grandParentNode.setRed(true);
                //4)将祖父节点当成插入节点继续判断
                insertNode = grandParentNode;
                parentNode = grandParentNode.getParentNode();

            }else{
                //2.3插入节点的叔父节点为空或者黑色
                if(insertNode == parentNode.getLeftNode() && grandParentNode.getRightNode() == parentNode){
                    //插入节点为父节点的左节点且父节点为祖父节点的右节点 需要先右旋再左旋
                    rightRotate(insertNode);
                    leftRotate(insertNode);
                    return;
                }
                if(insertNode == parentNode.getLeftNode() && grandParentNode.getLeftNode() == parentNode){
                    //插入节点为父节点的左节点且父节点为祖父节点的左节点 只需要右旋一次
                    rightRotate(parentNode);
                    return;
                }
                if(insertNode == parentNode.getRightNode() && grandParentNode.getRightNode() == parentNode){
                    //插入节点为父节点的右节点且父节点为祖父节点的右节点 只需要左旋一次
                    leftRotate(parentNode);
                    return;
                }
                if(insertNode == parentNode.getRightNode() && grandParentNode.getLeftNode() == parentNode){
                    //插入节点为父节点的右节点且父节点为祖父节点的左节点 需要先左旋再右旋一次
                    leftRotate(insertNode);
                    rightRotate(insertNode);
                    return;
                }
            }
        }

    }

    /**
     * 将当前元素插入到红黑树中
     * @param t 元素
     * @return
     */
    private RBTreeNode insertNode(T t){
        //1.将当前节点插入到红黑树中
        if(root == null){
            //根节点必须为黑色节点
            root = new RBTreeNode<>(t,false);
            return root;
        }
        RBTreeNode<T> tempNode = root;//临时节点
        RBTreeNode<T> insertNode = null;//插入节点
        while (tempNode != null){
            if(t.hashCode() < tempNode.getVal().hashCode()){
                //如果插入节点值小于遍历节点值往左插入
                RBTreeNode<T> leftNode = tempNode.getLeftNode();
                if(leftNode == null){
                    //左节点为空赋值操作 插入节点父节点为遍历节点且颜色必须为红色,遍历节点左节点值赋值为插入节点
                    insertNode = new RBTreeNode<>(t, true, tempNode);
                    tempNode.setLeftNode(insertNode);
                    break;
                }
                //左节点不为空继续往下遍历
                tempNode = leftNode;
            }else{
                //如果插入节点值大于等于遍历节点值往右插入
                RBTreeNode<T> rightNode = tempNode.getRightNode();
                if(rightNode == null){
                    //右节点为空赋值操作 插入节点父节点为遍历节点且颜色必须为红色,遍历节点右节点值赋值为插入节点
                    insertNode = new RBTreeNode<>(t, true, tempNode);
                    tempNode.setRightNode(insertNode);
                    break;
                }
                //右节点不为空继续往下遍历
                tempNode = rightNode;
            }
        }
        return insertNode;
    }


    /**
     * 左旋
     * 旋转前 -> 当前节点为父节点的右节点
     * 旋转后 -> 父节点变为当前节点的左节点
     * @param curNode  当前节点--旋转轴点
     */
    private void leftRotate(RBTreeNode<T> curNode){
        //左旋一次处理
        RBTreeNode<T> parentNode = curNode.getParentNode();
        RBTreeNode<T> leftNode = curNode.getLeftNode();
        //1)存在祖父节点,则祖父节点的左/右节点为当前节点,否则说明祖父节点是根节点
        RBTreeNode<T> grandParentNode = parentNode.getParentNode();
        if(grandParentNode != null) {
            if(grandParentNode.getLeftNode() == parentNode){
                grandParentNode.setLeftNode(curNode);
            }else{
                grandParentNode.setRightNode(curNode);
            }
        }else{
            root = curNode;
        }
        //2)当前节点的现父节点为祖父节点 且左节点为原父节点 颜色变黑
        curNode.setParentNode(grandParentNode);
        curNode.setLeftNode(parentNode);
        curNode.setRed(false);
        //3)原父节点的现父节点为当前节点 且右节点为当前节点的原左节点 颜色变红
        parentNode.setParentNode(curNode);
        parentNode.setRightNode(leftNode);
        parentNode.setRed(true);
        //4)当前节点的原左节点的现父节点为当前节点的原父节点
        if(leftNode != null){
            leftNode.setParentNode(parentNode);
        }
    }

    /**
     * 右旋
     * 旋转前 -> 当前节点为父节点的左节点
     * 旋转后 -> 父节点变为当前节点的右节点
     * @param curNode  当前节点--旋转轴点
     */
    private void rightRotate(RBTreeNode<T> curNode){
        //右旋一次处理
        RBTreeNode<T> parentNode = curNode.getParentNode();
        RBTreeNode<T> rightNode = curNode.getRightNode();
        //1)存在祖父节点,则祖父节点的左/右节点为当前节点,否则说明祖父节点是根节点
         RBTreeNode<T> grandParentNode = parentNode.getParentNode();
        if(grandParentNode != null){
            if(grandParentNode.getLeftNode() == parentNode){
                grandParentNode.setLeftNode(curNode);
            }else{
                grandParentNode.setRightNode(curNode);
            }
        }else{
            root = curNode;
        }
        //2)当前节点的父节点为祖父节点 且右节点为原父节点 颜色变黑
        curNode.setParentNode(grandParentNode);
        curNode.setRightNode(parentNode);
        curNode.setRed(false);
        //3)原父节点的现父节点为当前节点 且左节点为当前节点的原右节点 颜色变红
        parentNode.setParentNode(curNode);
        parentNode.setLeftNode(rightNode);
        parentNode.setRed(true);
        //4)当前节点的原右节点的现父节点为当前节点的原父节点
        if(rightNode != null){
            rightNode.setParentNode(parentNode);
        }
    }

    public void iterator(){
        if(root == null){
            System.out.println("无节点元素");
            return;
        }
        System.out.println(String.format("根节点元素:%s",root.getVal()));
        root.middleIterator();
    }

    public void iterator(List<Circular> circularList, List<Line> lineList){
        if(root == null){
            System.out.println("无节点元素");
            return;
        }
        System.out.println(String.format("根节点元素:%s",root.getVal()));
        root.middleIterator(circularList,lineList,new Coordinate());
    }


}

步骤二:绘制红黑树

各节点坐标

package rbtree;


public class Circular {
    private int x;
    private int y;
    private String val;
    private boolean red;

    public Circular() {
    }

    public Circular(int x, int y, String val, boolean red) {
        this.x = x;
        this.y = y;
        this.val = val;
        this.red = red;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public String getVal() {
        return val;
    }

    public void setVal(String val) {
        this.val = val;
    }

    public boolean isRed() {
        return red;
    }

    public void setRed(boolean red) {
        this.red = red;
    }
}

节点与节点之间连线关系

package rbtree;


public class Line {
    private int x1;
    private int y1;
    private int x2;
    private int y2;

    public Line() {
    }

    public Line(int x1, int y1, int x2, int y2) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
    }

    public int getX1() {
        return x1;
    }

    public void setX1(int x1) {
        this.x1 = x1;
    }

    public int getY1() {
        return y1;
    }

    public void setY1(int y1) {
        this.y1 = y1;
    }

    public int getX2() {
        return x2;
    }

    public void setX2(int x2) {
        this.x2 = x2;
    }

    public int getY2() {
        return y2;
    }

    public void setY2(int y2) {
        this.y2 = y2;
    }

    @Override
    public String toString() {
        return "Line{" +
                "x1=" + x1 +
                ", y1=" + y1 +
                ", x2=" + x2 +
                ", y2=" + y2 +
                '}';
    }
}

中序遍历(递归遍历)临时坐标

package rbtree;

public class Coordinate {
    private int nextX;//下一遍历的节点的X坐标
    private int curX;//最后一个遍历的节点的X坐标
    private int curY;//当前遍历节点的Y坐标

    public int getNextX() {
        return nextX;
    }

    public void setNextX(int nextX) {
        this.nextX = nextX;
    }

    public int getCurX() {
        return curX;
    }

    public void setCurX(int curX) {
        this.curX = curX;
    }

    public int getCurY() {
        return curY;
    }

    public void setCurY(int curY) {
        this.curY = curY;
    }
}

面板

package rbtree;

import java.awt.*;

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;
import java.util.List;


public class RBTreeFrame<T> extends Frame {

    /**
     * 面板坐标以及宽高
     */
    private static final int FRAME_WIDTH = 1800;
    private static final int FRAME_HEIGHT = 800;
    private static final int FRAME_X = 100;
    private static final int FRAME_Y = 100;

    /**
     * 圆形半径
     */
    private static final int CIRCULAR_RADIUS = 50;

    /**
     *偏移位置
     */
    private static final int X = 50;
    private static final int Y = 50;

    /**
     * 字体
     */

    private static final int FONT_SIZE = 16;
    private static final String FONT_NAME = "宋体";

    private List<Circular> circularList;

    private List<Line> lineList;




    public void showFrame(List<Circular> circularList, List<Line> lineList) {
        this.circularList = circularList;
        this.lineList = lineList;
        setBounds(FRAME_X,FRAME_Y,FRAME_WIDTH,FRAME_HEIGHT);
        this.addWindowListener(new WindowAdapter(){
        public void windowClosing(WindowEvent e) {
            System.exit(0);

        }

        });
        setVisible(true);

    }


    public void paint(Graphics g) {
        Font font = new Font(FONT_NAME,Font.BOLD,FONT_SIZE);	//加粗
        for(Line line : lineList){
            g.setColor(Color.BLACK);
            g.drawLine((line.getX1() + 1) * X  + CIRCULAR_RADIUS/2,(line.getY1() + 1) * X + CIRCULAR_RADIUS/2,(line.getX2() + 1) * X + CIRCULAR_RADIUS/2,(line.getY2() + 1) * X + CIRCULAR_RADIUS/2);
        }
        for(Circular circular : circularList){
            if(circular.isRed()){
                g.setColor(Color.RED);
            }else{
                g.setColor(Color.BLACK);
            }
            int x = (circular.getX() + 1) * X;
            int y = (circular.getY() + 1) * Y;

            g.fillOval(x, y, CIRCULAR_RADIUS, CIRCULAR_RADIUS);
            g.setColor(Color.WHITE);
            g.setFont(font);
            int len = circular.getVal().length();
            if(len == 1){
                g.drawString(circular.getVal(),x + CIRCULAR_RADIUS/2 - 6,y+CIRCULAR_RADIUS/2 + 6);
            }else if(len == 2){
                g.drawString(circular.getVal(),x + CIRCULAR_RADIUS/2 - 9,y+CIRCULAR_RADIUS/2 + 6);
            }else{
                g.drawString(circular.getVal(),x + CIRCULAR_RADIUS/2 - 13,y+CIRCULAR_RADIUS/2 + 6);
            }
        }


    }

}

步骤三: 测试

测试类

package rbtree;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


public class Demo {
    public static void main(String[] args) {
        int num = 30;
        Random random = new Random();
        RBTree<Integer> rbTree = new RBTree<>();
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < num; i++){
            int randomNum = random.nextInt(1000);
            list.add(randomNum);
            rbTree.addNode(randomNum);
        }
        System.out.println(list);
        //rbTree.iterator();
        List<Circular> circularList = new ArrayList<>();
        List<Line> lineList = new ArrayList<>();
        rbTree.iterator(circularList,lineList);
        if(circularList.size() > 0){
            new RBTreeFrame().showFrame(circularList,lineList);
        }

    }
}

效果图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

补充

1)目前实现不考虑删除节点。
2)基本是通过红黑树链接插入数据,看效果图,找规律(自己总结理解起来更深刻)。
3)为了方便验证自己写的红黑树没有问题,并把红黑树绘制出来相对更加直观,但不确保百分之百没有问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值