效果
一.红黑树节点类定义
public class Node {
}
1.1 颜色使用Boolen定义
public static boolean BLACK = true;
public static boolean RED = false;
1.2 每一个节点有值、父节点、左右节点、颜色等属性
private int value;
private Node left;
private Node right;
private Node parent;
private Boolean color;
1.3 构造方法和toSting方法
tostring方法用于显示树结构显示的字符串
public Node(int value) {
this.value = value;
this.color = RED;
}
@Override
public String toString() {
String colorStr = color ? "B" : "R";
return value + colorStr;
}
二.树打印类,用于验证树结构是否正确
目标效果:
打印树结构,我们使用二维数组来组成全空格的画板,将树结构写入二维数组的相应位置中显示出来
public class TreeOperation {
}
2.1 获取树高度
获取树高度用于确定二维数组的高度
// 用于获得树的层数
public static int getTreeDepth(Node root) {
return root == null ? 0 : (1 + Math.max(getTreeDepth(root.getLeft()), getTreeDepth(root.getRight())));
}
2.2 写二维数组
这个方法是递归方法,将每个节点写入二维数组,从树的根节点开始执行,递归执行到每一个节点。
rowIndex是节点要写入的行坐标,columnIndex是列坐标,res是存储的二维数组,treeDepth是树高
主要逻辑就是先确定一个节点的位置,根据这个节点位置,画出左右枝干,左右枝干就在节点下层的两侧,具体宽度可以调节,再根据枝干位置,给出子节点的位置,以此类推
还有一定要注意,节点与枝干的宽度是递减的,不然节点写入二维数组的位置会重叠
private static void writeArray(Node currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
// 保证输入的树不为空
if (currNode == null) {
return;
}
// 先将当前节点保存到二维数组中
// rowIndex是层数,columnIndex是列数
res[rowIndex][columnIndex] = String.valueOf(currNode.getValue() + (currNode.getColor() ? "B" : "R") + "");
// 计算当前位于树的第几层
//因为还有斜线,如果行数+1除以2等于数据层数,rowIndex由于下面代码的判断会跳过单数数字
int currLevel = ((rowIndex + 1) / 2);
// 若到了最后一层,则返回
if (currLevel == treeDepth) {
return;
}
// 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
// -1是由于层数是从0开始的,treeDepth是从1开始的,因为上面的宽度要容纳很多,所以上面的间隔要大于下面的隔间
//越是子节点越小间隔
int gap = treeDepth - currLevel - 1;
// int gap = 4;
// 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
if (currNode.getLeft() != null) {
// columnIndex-与上一层元素的间隔
if (rowIndex == 0) {
res[rowIndex + 1][columnIndex - gap * 5] = "/";
writeArray(currNode.getLeft(), rowIndex + 2, columnIndex - gap * 5, res, treeDepth);
} else {
res[rowIndex + 1][columnIndex - gap] = "/";
writeArray(currNode.getLeft(), rowIndex + 2, columnIndex - gap * 3, res, treeDepth);
}
}
// 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
if (currNode.getRight() != null) {
if (rowIndex == 0) {
res[rowIndex + 1][columnIndex + gap * 5] = "\\";
writeArray(currNode.getRight(), rowIndex + 2, columnIndex + gap * 5, res, treeDepth);
} else {
res[rowIndex + 1][columnIndex + gap] = "\\";
writeArray(currNode.getRight(), rowIndex + 2, columnIndex + gap * 3, res, treeDepth);
}
}
}
2.3 打印方法
传入根节点进行打印树结构。
难理解处:
1.int arrayHeight = treeDepth * 2 - 1;
高度每个节点都有枝干,最后一层节点没有枝干,少一层的枝干,所以是2*树高-1
2.int arrayWidth = (2 << (treeDepth - 2)) * 9 + 1;
宽度,每一层的节点数是2^(n-1)=2 << (treeDepth - 2), 乘以9是每个节点+空格需要9个空格的位置,+1是开始的空格。9和1可以自己调整。
public static void show(Node root) {
if (root == null) {
System.out.println("EMPTY!");
}
// 得到树的深度
int treeDepth = getTreeDepth(root);
// 最后一行的宽度为2的(n - 1)次方乘3,再加1
// 作为整个二维数组的宽度
//因为数据需要treeDepth层,加上画线treeDepth-1(最后一层数据不用画线)
int arrayHeight = treeDepth * 2 - 1;
//2 <<N :2*2^N 2*2^treeDepth-2=2^n-1 第一层是1 第二层是2,第五层就是16
//乘以3是每一个数据要占用+旁边的空格要占用三个?
int arrayWidth = (2 << (treeDepth - 2)) * 9 + 1;
// 用一个字符串数组来存储每个位置应显示的元素
String[][] res = new String[arrayHeight][arrayWidth];
// 对数组进行初始化,默认为一个空格
for (int i = 0; i < arrayHeight; i++) {
for (int j = 0; j < arrayWidth; j++) {
res[i][j] = " ";
}
}
// 从根节点开始,递归处理整个树
// res[0][(arrayWidth + 1)/ 2] = (char)(root.val +