步骤一:实现红黑树
定义红黑树节点类
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)为了方便验证自己写的红黑树没有问题,并把红黑树绘制出来相对更加直观,但不确保百分之百没有问题。
265

被折叠的 条评论
为什么被折叠?



