public class TreeNode < T > {
T value;
TreeNode < T > left;
TreeNode < T > right;
public TreeNode ( ) {
}
public TreeNode ( T value) {
this . value = value;
}
public T getValue ( ) {
return value;
}
public void setValue ( T value) {
this . value = value;
}
public TreeNode < T > getLeft ( ) {
return left;
}
public void setLeft ( TreeNode < T > left) {
this . left = left;
}
public TreeNode < T > getRight ( ) {
return right;
}
public void setRight ( TreeNode < T > right) {
this . right = right;
}
@Override
public String toString ( ) {
return "TreeNode{" +
"value=" + value +
", leftChild=" + left +
", rightChild=" + right +
'}' ;
}
@Override
public boolean equals ( Object o) {
if ( this == o) {
return true ;
}
if ( ! ( o instanceof TreeNode ) ) {
return false ;
}
TreeNode < ? > treeNode = ( TreeNode < ? > ) o;
return getValue ( ) . equals ( treeNode. getValue ( ) ) && Objects . equals ( getLeft ( ) , treeNode. getLeft ( ) ) && Objects . equals ( getRight ( ) , treeNode. getRight ( ) ) ;
}
@Override
public int hashCode ( ) {
return Objects . hash ( getValue ( ) , getLeft ( ) , getRight ( ) ) ;
}
}
public static < T > void printBinaryTreeVisually ( TreeNode < T > tree) {
if ( tree == null ) {
return ;
}
int depth = findDepth ( tree) ;
PrintNode < T > printTree = convertToPrintNode ( tree, null , depth, 0 , null ) ;
printTree. width = findWidth ( printTree, 0 ) ;
List < List < PrintNode < T > > > nodesList = new ArrayList < > ( ) ;
Queue < PrintNode < T > > queue = new LinkedList < > ( ) ;
queue. add ( printTree) ;
while ( ! queue. isEmpty ( ) ) {
List < PrintNode < T > > curLevel = new ArrayList < > ( ) ;
while ( ! queue. isEmpty ( ) ) {
PrintNode < T > cur = queue. poll ( ) ;
if ( cur. parent != null ) {
PrintNode < T > parent = cur. parent;
cur. width = parent. width;
}
curLevel. add ( cur) ;
}
nodesList. add ( curLevel) ;
for ( PrintNode < T > node : curLevel) {
if ( node. left != null ) {
queue. add ( node. left) ;
}
if ( node. right != null ) {
queue. add ( node. right) ;
}
}
}
List < PrintNode < T > > lastLevel = nodesList. get ( depth - 1 ) ;
for ( int i = 0 ; i < lastLevel. size ( ) ; i++ ) {
PrintNode < T > first = lastLevel. get ( i) ;
if ( ! first. isVirtual) {
first. offsetX = 0 ;
for ( int j = i - 1 ; j >= 0 ; j-- ) {
lastLevel. get ( j) . offsetX = lastLevel. get ( j + 1 ) . offsetX - first. width;
}
for ( int k = i + 1 ; k < lastLevel. size ( ) ; k++ ) {
lastLevel. get ( k) . offsetX = lastLevel. get ( k - 1 ) . offsetX + first. width;
}
break ;
}
}
for ( int i = depth - 2 ; i >= 0 ; i-- ) {
List < PrintNode < T > > level = nodesList. get ( i) ;
for ( PrintNode < T > node : level) {
node. offsetX = ( node. left. offsetX + node. right. offsetX) / 2 ;
}
}
int offsetSup = findOffsetLeft ( printTree, 0 ) ;
fixOffset ( printTree, offsetSup) ;
for ( List < PrintNode < T > > printNodes : nodesList) {
int x = 0 ;
for ( PrintNode < T > printNode : printNodes) {
if ( printNode. isVirtual) {
continue ;
}
StringBuilder fillers = new StringBuilder ( ) ;
for ( ; x < printNode. offsetX; x++ ) {
fillers. append ( " " ) ;
}
System . out. print ( fillers) ;
System . out. print ( printNode. value) ;
x += printNode. selfWidth;
}
System . out. println ( ) ;
int y = 0 ;
String filler = " " ;
for ( PrintNode < T > printNode : printNodes) {
if ( printNode. isVirtual || ( printNode. left == null && printNode. right == null ) || ( printNode. left. isVirtual && printNode. right. isVirtual) ) {
continue ;
}
StringBuilder fillers = new StringBuilder ( ) ;
for ( ; y < printNode. offsetX; y++ ) {
if ( printNode. left != null && ! printNode. left. isVirtual && printNode. left. offsetX == y) {
fillers. append ( "┌" ) ;
filler = "—" ;
continue ;
}
fillers. append ( filler) ;
}
if ( printNode. left != null && ! printNode. left. isVirtual && printNode. right != null && ! printNode. right. isVirtual) {
fillers. append ( "┴" ) ;
} else if ( printNode. left != null && ! printNode. left. isVirtual) {
fillers. append ( "┘" ) ;
} else if ( printNode. right != null && ! printNode. right. isVirtual) {
fillers. append ( "└" ) ;
}
if ( printNode. right != null && ! printNode. right. isVirtual) {
for ( ; y < printNode. right. offsetX; y++ ) {
filler = "—" ;
if ( printNode. right. offsetX - 1 == y) {
fillers. append ( "┐" ) ;
filler = " " ;
continue ;
}
fillers. append ( filler) ;
}
}
System . out. print ( fillers) ;
filler = " " ;
y++ ;
}
System . out. println ( ) ;
}
}
private static < T > PrintNode < T > convertToPrintNode ( TreeNode < T > tree, PrintNode < T > parent, int depth, int curDepth, Boolean isLeft) {
PrintNode < T > cur = new PrintNode < > ( ) ;
++ curDepth;
if ( tree == null ) {
if ( curDepth > depth) {
return null ;
}
cur. isVirtual = true ;
tree = new PrintNode < > ( ) ;
}
cur. value = tree. value;
if ( cur. value != null ) {
cur. selfWidth = cur. value. toString ( ) . length ( ) ;
}
cur. parent = parent;
cur. isLeft = isLeft;
cur. left = convertToPrintNode ( tree. left, cur, depth, curDepth, true ) ;
cur. right = convertToPrintNode ( tree. right, cur, depth, curDepth, false ) ;
return cur;
}
private static < T > int findWidth ( TreeNode < T > tree, int max) {
if ( tree == null ) {
return 0 ;
}
max = Math . max ( tree. value. toString ( ) . length ( ) + 1 , max) ;
if ( max % 2 == 1 ) {
max++ ;
}
int left = 0 ;
int right = 0 ;
if ( tree. left != null ) {
left = findWidth ( tree. left, max) ;
}
if ( tree. right != null ) {
right = findWidth ( tree. right, max) ;
}
return Math . max ( Math . max ( left, right) , max) ;
}
public static < T > int findDepth ( TreeNode < T > tree) {
return findDepth ( tree, 0 ) ;
}
private static < T > int findDepth ( TreeNode < T > tree, int depth) {
if ( tree == null ) {
return 0 ;
}
depth++ ;
int leftDepth = 0 ;
int rightDepth = 0 ;
if ( tree. left != null ) {
leftDepth = findDepth ( tree. left, depth) ;
}
if ( tree. right != null ) {
rightDepth = findDepth ( tree. right, depth) ;
}
return Math . max ( Math . max ( leftDepth, rightDepth) , depth) ;
}
private static < T > int findOffsetLeft ( PrintNode < T > tree, int offset) {
if ( tree == null || tree. isVirtual) {
return 0 ;
}
offset = Math . max ( - tree. offsetX, offset) ;
int offsetL = 0 ;
int offsetR = 0 ;
if ( tree. left != null ) {
offsetL = findOffsetLeft ( tree. left, offset) ;
}
if ( tree. right != null ) {
offsetR = findOffsetLeft ( tree. right, offset) ;
}
return Math . max ( Math . max ( offsetL, offsetR) , offset) ;
}
private static < T > void fixOffset ( PrintNode < T > tree, int offset) {
if ( tree == null ) {
return ;
}
tree. offsetX += offset;
if ( tree. left != null ) {
fixOffset ( tree. left, offset) ;
}
if ( tree. right != null ) {
fixOffset ( tree. right, offset) ;
}
}
public static class PrintNode < T > extends TreeNode < T > {
PrintNode < T > left;
PrintNode < T > right;
int width;
int selfWidth;
int offsetX;
PrintNode < T > parent;
boolean isVirtual;
Boolean isLeft;
}
测试
TreeNode < String > tree = new TreeNode < > ( ) ;
Random random = new Random ( ) ;
List < String > strings = new ArrayList < > ( ) ;
strings. add ( "M" ) ;
strings. add ( "Java" ) ;
strings. add ( "Tomcat" ) ;
strings. add ( "Linux" ) ;
strings. add ( "Elastic" ) ;
strings. add ( "rm/*-rf" ) ;
strings. add ( "Tom" ) ;
strings. add ( "Apollo" ) ;
strings. add ( "0_0" ) ;
strings. add ( "Eureka" ) ;
Queue < TreeNode < String > > stack = new LinkedList < > ( ) ;
stack. add ( tree) ;
tree. value = "HetFrame" ;
TreeNode < String > cur;
Queue < TreeNode < String > > nodes = new LinkedList < > ( ) ;
for ( int i = 0 ; i < 20 ; i++ ) {
TreeNode < String > newNode = new TreeNode < > ( strings. get ( random. nextInt ( 10 ) ) ) ;
nodes. add ( newNode) ;
}
while ( ! stack. isEmpty ( ) ) {
cur = stack. poll ( ) ;
int ran = random. nextInt ( 10 ) ;
if ( nodes. isEmpty ( ) ) {
continue ;
}
cur. left = nodes. poll ( ) ;
cur. right = nodes. poll ( ) ;
if ( ran < 3 ) {
cur. left = null ;
}
if ( ran > 8 ) {
cur. right = null ;
}
if ( cur. left != null ) {
stack. add ( cur. left) ;
}
if ( cur. right != null ) {
stack. add ( cur. right) ;
}
}
printBinaryTreeVisually ( tree) ;
结果
HetFrame
┌———————————————————————————————————————┴———————————————————————————————————————┐
Java Tom
┌———————————————————┴———————————————————┐ ┌———————————————————┴———————————————————┐
Java Eureka Linux 0_0
└—————————┐ └—————————┐ └—————————┐ └—————————┐
Apollo rm/*-rf Tom Elastic
┌————┘ ┌————┴————┐ ┌————┴————┐
Eureka Java Tom Linux Apollo