原理部分
实现效果如下:

在控制台打印时,是从上往下一行一行打印的;上面的效果图是将二叉树一层的节点在同一行中打印,因此在遍历时应该层序遍历:将同一层的节点按顺序遍历打印,再继续遍历下一层的节点;
在打印同一层的节点时,要计算好每个节点之间的间隔距离,同一层节点之间的间隔可以看节点之间的横坐标之差;要计算出横坐标之差,就要得到每一个节点的横坐标;

如果按照: XA —> XG 的顺序将节点存储到数组中,那么每个节点对应的数组下标可以看作是节点的横坐标;在二叉树中,中序遍历的结果恰好是这个顺序;

在打印完节点之后,还要打印链接子节点的线段;同一层节点之间有多个节点,因此会有多条链接子节点的线段;而这些线段也在同一层中,因此也要将这些线段拼接在一行中一起打印;

每一条链接子节点线段的长度如何确定呢?也很简单,子节点的坐标与当前节点坐标 之差就是线段的长度;因为节点可能会缺失左节点或者右节点,因此要分别计算链接左右子节点的线段长度;
多条线段之间的间隔也要计算出来,这个就比较简单了两条线段之间的间隔: distance = leftIndex - lastRightIndex;

以上图为例,如果节点1没有right节点,那么lastRightIndex = 节点1的坐标;如果节点5没有left节点,那么leftIndex = 节点5的坐标 ;
同一层节点要打印的部分:

- 第一部分是数据节点上的竖线;
- 第二部分是,数据打印
- 第三部分是连接线;
代码
- 中序遍历,找到每个节点的坐标;
List<Node> mid = new ArrayList<>();
void midOrder(Node node){
if(node == null)return;
midOrder(node.left);
mid.add(node);
midOrder(node.right);
}
//将node ,index ====> 存入map中
Map<Node,Integer> map =new HashMap<>();
void init(){
if(root == null)return;
midOrder(root);
for (int i = 0; i < mid.size(); i++) {
map.put(mid.get(i),i);
}
}
- 层序遍历打印
void treePrint(List<Node> nodes){
if(nodes.isEmpty())return;
// nodes : 同一层节点
printLevel(nodes);//打印同一层的节点
List<Node> children = new ArrayList<>();
//顺序遍历下一层节点;
for (Node node : nodes) {
if(node.left != null)children.add(node.left);
if(node.right != null) children.add(node.right);
}
treePrint(children);//递归打印下一层节点
}
- 打印同一层节点
void printLevel(List<Node> nodes){
String VLine = "";//节点头上的竖线
String dataLine = "";// 同一层的节点数字
String line = "";//这一层所有节点链接子节点的线段
int lastNodeIndex= 0;
int lastRightIndex = 0;
for (Node node : nodes) {
int x = map.get(node);//当前节点的坐标
String addEmpty = "";
//lastNodeIndex:上一个节点的坐标
//获取相邻节点之间的距离,用 \t 填充;
for (int i = lastNodeIndex; i <x ; i++) {
addEmpty+="\t";
}
lastNodeIndex = x;
VLine += addEmpty+"|";//竖线拼接
//数字拼接
dataLine+= addEmpty+node.data;
//如果是红黑树,还可以根据下面代码,打印出红色节点
// if(node.red)
// dataLine+= addEmpty +"\033[91;1m"+node.data+"\033[0m";//打印红色
// else
// dataLine += addEmpty+node.data;//打印黑色
//--------------------------------------------------------------计算子节点的链接线
Node left = node.left;
Node right = node.right;
String leftLine = null;
String rightLine = null;
int leftIndex = -1;
int rightIndex = -1;
if(left != null){
leftIndex = map.get(left);
leftLine = getLineToSon(x - leftIndex);
}
if(right != null){
rightIndex = map.get(right);
rightLine = getLineToSon(rightIndex - x);
}
//子节点线段
String curLine = (leftLine == null ? "" :leftLine) + "|"+(rightLine == null ? "" : rightLine);
if(leftLine == null && rightLine == null)curLine="";
//线段之间的间隔
int dif = (leftIndex == -1 ? x : leftIndex) - lastRightIndex;
String difEmpty = "";
for (int i = 0; i < dif; i++) {
difEmpty+="\t";
}
//拼接线段,与之前的线段拼接
line += difEmpty + curLine;
lastRightIndex = rightIndex == -1 ? x : rightIndex;
}
System.out.println(VLine);//打印竖线
System.out.println(dataLine);//打印数字
System.out.println(line);//打印链接子节点线段
}
//链接子线段的长度
String getLineToSon(int end){
String line = "";
if(end ==0)return line;
for (int i = 0; i < end; i++) {
line+="____";
}
return line;
}
- 打印测试
public void treePrint(){
init();
List<Node> nodes = new ArrayList<>();
nodes.add(root);
treePrint(nodes);
}
@Test
public void test(){
for (int i = 0; i < 10; i++) {
addVal(i);
}
System.out.println("\n++++++++++++++++TreePrint test+++++++++++++++");
treePrint();
}
- 结果

红黑树和打印测试代码:Gitee