如何打印二叉树图?
如何在Java中打印二叉树,使输出如下所示:
4 / \ 2 5
我的节点:
public class Node<A extends Comparable> { Node<A> left, right; A data; public Node(A data){ this.data = data; } }
我创build了简单的二叉树打印机。 您可以随意使用和修改它,但不会进行优化。 我认为这里可以改进很多东西;)
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BTreePrinterTest { private static Node<Integer> test1() { Node<Integer> root = new Node<Integer>(2); Node<Integer> n11 = new Node<Integer>(7); Node<Integer> n12 = new Node<Integer>(5); Node<Integer> n21 = new Node<Integer>(2); Node<Integer> n22 = new Node<Integer>(6); Node<Integer> n23 = new Node<Integer>(3); Node<Integer> n24 = new Node<Integer>(6); Node<Integer> n31 = new Node<Integer>(5); Node<Integer> n32 = new Node<Integer>(8); Node<Integer> n33 = new Node<Integer>(4); Node<Integer> n34 = new Node<Integer>(5); Node<Integer> n35 = new Node<Integer>(8); Node<Integer> n36 = new Node<Integer>(4); Node<Integer> n37 = new Node<Integer>(5); Node<Integer> n38 = new Node<Integer>(8); root.left = n11; root.right = n12; n11.left = n21; n11.right = n22; n12.left = n23; n12.right = n24; n21.left = n31; n21.right = n32; n22.left = n33; n22.right = n34; n23.left = n35; n23.right = n36; n24.left = n37; n24.right = n38; return root; } private static Node<Integer> test2() { Node<Integer> root = new Node<Integer>(2); Node<Integer> n11 = new Node<Integer>(7); Node<Integer> n12 = new Node<Integer>(5); Node<Integer> n21 = new Node<Integer>(2); Node<Integer> n22 = new Node<Integer>(6); Node<Integer> n23 = new Node<Integer>(9); Node<Integer> n31 = new Node<Integer>(5); Node<Integer> n32 = new Node<Integer>(8); Node<Integer> n33 = new Node<Integer>(4); root.left = n11; root.right = n12; n11.left = n21; n11.right = n22; n12.right = n23; n22.left = n31; n22.right = n32; n23.left = n33; return root; } public static void main(String[] args) { BTreePrinter.printNode(test1()); BTreePrinter.printNode(test2()); } } class Node<T extends Comparable<?>> { Node<T> left, right; T data; public Node(T data) { this.data = data; } } class BTreePrinter { public static <T extends Comparable<?>> void printNode(Node<T> root) { int maxLevel = BTreePrinter.maxLevel(root); printNodeInternal(Collections.singletonList(root), 1, maxLevel); } private static <T extends Comparable<?>> void printNodeInternal(List<Node<T>> nodes, int level, int maxLevel) { if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes)) return; int floor = maxLevel - level; int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); int firstSpaces = (int) Math.pow(2, (floor)) - 1; int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; BTreePrinter.printWhitespaces(firstSpaces); List<Node<T>> newNodes = new ArrayList<Node<T>>(); for (Node<T> node : nodes) { if (node != null) { System.out.print(node.data); newNodes.add(node.left); newNodes.add(node.right); } else { newNodes.add(null); newNodes.add(null); System.out.print(" "); } BTreePrinter.printWhitespaces(betweenSpaces); } System.out.println(""); for (int i = 1; i <= endgeLines; i++) { for (int j = 0; j < nodes.size(); j++) { BTreePrinter.printWhitespaces(firstSpaces - i); if (nodes.get(j) == null) { BTreePrinter.printWhitespaces(endgeLines + endgeLines + i + 1); continue; } if (nodes.get(j).left != null) System.out.print("/"); else BTreePrinter.printWhitespaces(1); BTreePrinter.printWhitespaces(i + i - 1); if (nodes.get(j).right != null) System.out.print("\\"); else BTreePrinter.printWhitespaces(1); BTreePrinter.printWhitespaces(endgeLines + endgeLines - i); } System.out.println(""); } printNodeInternal(newNodes, level + 1, maxLevel); } private static void printWhitespaces(int count) { for (int i = 0; i < count; i++) System.out.print(" "); } private static <T extends Comparable<?>> int maxLevel(Node<T> node) { if (node == null) return 0; return Math.max(BTreePrinter.maxLevel(node.left), BTreePrinter.maxLevel(node.right)) + 1; } private static <T> boolean isAllElementsNull(List<T> list) { for (Object object : list) { if (object != null) return false; } return true; } }
输出1:
2 / \ / \ / \ / \ 7 5 / \ / \ / \ / \ 2 6 3 6 / \ / \ / \ / \ 5 8 4 5 8 4 5 8
输出2:
2 / \ / \ / \ / \ 7 5 / \ \ / \ \ 2 6 9 / \ / 5 8 4
按行打印[大]树。
输出示例:
└── z ├── c │ ├── a │ └── b ├── d ├── e │ └── asdf └── f
码:
public class TreeNode { final String name; final List<TreeNode> children; public TreeNode(String name, List<TreeNode> children) { this.name = name; this.children = children; } public void print() { print("", true); } private void print(String prefix, boolean isTail) { System.out.println(prefix + (isTail ? "└── " : "├── ") + name); for (int i = 0; i < children.size() - 1; i++) { children.get(i).print(prefix + (isTail ? " " : "│ "), false); } if (children.size() > 0) { children.get(children.size() - 1) .print(prefix + (isTail ?" " : "│ "), true); } } }
PS对不起,这个答案并不完全关注“二元”树。 当请求打印一棵树时,它只会被googlesearch到。 解决scheme的灵感来自于linux中的“树”命令。
public static class Node<T extends Comparable<T>> { T value; Node<T> left, right; public void insertToTree(T v) { if (value == null) { value = v; return; } if (v.compareTo(value) < 0) { if (left == null) { left = new Node<T>(); } left.insertToTree(v); } else { if (right == null) { right = new Node<T>(); } right.insertToTree(v); } } public void printTree(OutputStreamWriter out) throws IOException { if (right != null) { right.printTree(out, true, ""); } printNodeValue(out); if (left != null) { left.printTree(out, false, ""); } } private void printNodeValue(OutputStreamWriter out) throws IOException { if (value == null) { out.write("<null>"); } else { out.write(value.toString()); } out.write('\n'); } // use string and not stringbuffer on purpose as we need to change the indent at each recursion private void printTree(OutputStreamWriter out, boolean isRight, String indent) throws IOException { if (right != null) { right.printTree(out, true, indent + (isRight ? " " : " | ")); } out.write(indent); if (isRight) { out.write(" /"); } else { out.write(" \\"); } out.write("----- "); printNodeValue(out); if (left != null) { left.printTree(out, false, indent + (isRight ? " | " : " ")); } } }
将打印:
/----- 20 | \----- 15 /----- 14 | \----- 13 /----- 12 | | /----- 11 | \----- 10 | \----- 9 8 | /----- 7 | /----- 6 | | \----- 5 \----- 4 | /----- 3 \----- 2 \----- 1
为input
8 4 12 2 6 10 14 1 3 5 7 9 11 13 20 15
这是@ anurag的答案的一个变种 – 这是看我多余的| s
我为此做了一个改进的algorithm,可以处理不同大小的节点。 它使用线条自上而下打印。
package alg; import java.util.ArrayList; import java.util.List; /** * Binary tree printer * * @author MightyPork */ public class TreePrinter { /** Node that can be printed */ public interface PrintableNode { /** Get left child */ PrintableNode getLeft(); /** Get right child */ PrintableNode getRight(); /** Get text to be printed */ String getText(); } /** * Print a tree * * @param root * tree root node */ public static void print(PrintableNode root) { List<List<String>> lines = new ArrayList<List<String>>(); List<PrintableNode> level = new ArrayList<PrintableNode>(); List<PrintableNode> next = new ArrayList<PrintableNode>(); level.add(root); int nn = 1; int widest = 0; while (nn != 0) { List<String> line = new ArrayList<String>(); nn = 0; for (PrintableNode n : level) { if (n == null) { line.add(null); next.add(null); next.add(null); } else { String aa = n.getText(); line.add(aa); if (aa.length() > widest) widest = aa.length(); next.add(n.getLeft()); next.add(n.getRight()); if (n.getLeft() != null) nn++; if (n.getRight() != null) nn++; } } if (widest % 2 == 1) widest++; lines.add(line); List<PrintableNode> tmp = level; level = next; next = tmp; next.clear(); } int perpiece = lines.get(lines.size() - 1).size() * (widest + 4); for (int i = 0; i < lines.size(); i++) { List<String> line = lines.get(i); int hpw = (int) Math.floor(perpiece / 2f) - 1; if (i > 0) { for (int j = 0; j < line.size(); j++) { // split node char c = ' '; if (j % 2 == 1) { if (line.get(j - 1) != null) { c = (line.get(j) != null) ? '┴' : '┘'; } else { if (j < line.size() && line.get(j) != null) c = '└'; } } System.out.print(c); // lines and spaces if (line.get(j) == null) { for (int k = 0; k < perpiece - 1; k++) { System.out.print(" "); } } else { for (int k = 0; k < hpw; k++) { System.out.print(j % 2 == 0 ? " " : "─"); } System.out.print(j % 2 == 0 ? "┌" : "┐"); for (int k = 0; k < hpw; k++) { System.out.print(j % 2 == 0 ? "─" : " "); } } } System.out.println(); } // print line of numbers for (int j = 0; j < line.size(); j++) { String f = line.get(j); if (f == null) f = ""; int gap1 = (int) Math.ceil(perpiece / 2f - f.length() / 2f); int gap2 = (int) Math.floor(perpiece / 2f - f.length() / 2f); // a number for (int k = 0; k < gap1; k++) { System.out.print(" "); } System.out.print(f); for (int k = 0; k < gap2; k++) { System.out.print(" "); } } System.out.println(); perpiece /= 2; } } }
为了使用这个树,让你的Node
类实现PrintableNode
。
示例输出:
2952:0 ┌───────────────────────┴───────────────────────┐ 1249:-1 5866:0 ┌───────────┴───────────┐ ┌───────────┴───────────┐ 491:-1 1572:0 4786:1 6190:0 ┌─────┘ └─────┐ ┌─────┴─────┐ 339:0 5717:0 6061:0 6271:0
改编自Vasya Novikov的答案 ,使其更加二元化 ,并使用StringBuilder
来提高效率(在Java中连接String
对象一般是低效的)。
public StringBuilder toString(StringBuilder prefix, boolean isTail, StringBuilder sb) { if(right!=null) { right.toString(new StringBuilder().append(prefix).append(isTail ? "│ " : " "), false, sb); } sb.append(prefix).append(isTail ? "└── " : "┌── ").append(value.toString()).append("\n"); if(left!=null) { left.toString(new StringBuilder().append(prefix).append(isTail ? " " : "│ "), true, sb); } return sb; } @Override public String toString() { return this.toString(new StringBuilder(), true, new StringBuilder()).toString(); }
输出:
│ ┌── 7 │ ┌── 6 │ │ └── 5 └── 4 │ ┌── 3 └── 2 └── 1 └── 0
michal.kreuzman不错的一个我不得不说。 当我发现这真的帮助了我时,我懒得自己制作一个程序,在网上search代码。 但是我很害怕看到它只能用于单个数字,就好像你要使用多个数字一样,因为你使用的是空格,而不是制表符,结构将会被放错地方,程序将失去它的使用。 至于我以后的代码,我需要一些更大的input(至less超过10),这对我并不起作用,在我没有find任何东西的时候在网上search了很多,我自己做了一个程序。 它现在有一些错误,现在我正在懒惰地纠正它们,但它打印非常漂亮,节点可以采取任何大的价值。
树不会像问题提到,但它是270度旋转:)
public static void printBinaryTree(TreeNode root, int level){ if(root==null) return; printBinaryTree(root.right, level+1); if(level!=0){ for(int i=0;i<level-1;i++) System.out.print("|\t"); System.out.println("|-------"+root.val); } else System.out.println(root.val); printBinaryTree(root.left, level+1); }
将这个函数放在你自己指定的TreeNode中,并保持初始值为0。
享受。 以下是一些示例输出。
| | |-------11 | |-------10 | | |-------9 |-------8 | | |-------7 | |-------6 | | |-------5 4 | |-------3 |-------2 | |-------1 | | | |-------10 | | |-------9 | |-------8 | | |-------7 |-------6 | |-------5 4 | |-------3 |-------2 | |-------1
唯一的问题是延长分支我会尽快解决问题,但到那时你也可以使用它。
你的树将需要每层的距离的两倍:
一个 / \ / \ / \ / \ 公元前 / \ / \ / \ / \ DEFG / \ / \ / \ / \ HIJKLMNO
您可以将您的树保存在一个数组数组中,每个深度有一个数组:
[A],[B,C],[d,E,F,G],[H,I,J,K,L,M,N,O]]
如果您的树未满,则需要在该数组中包含空值:
一个 / \ / \ / \ / \ 公元前 / \ / \ / \ / \ DEFG / \ \ / \ \ hiklmo [[a],[b,c],[d,e,f,g],[h,i,k,l,m,o]]
然后你可以迭代数组来打印你的树,在第一个元素之前和元素之间打印空间取决于深度和打印行,这取决于数组中相应的元素是否被填充。 如果您的值可以超过一个字符长,则需要在创build数组表示时find最长的值,并相应地乘以所有宽度和行数。
public void printPreety() { List<TreeNode> list = new ArrayList<TreeNode>(); list.add(head); printTree(list, getHeight(head)); } public int getHeight(TreeNode head) { if (head == null) { return 0; } else { return 1 + Math.max(getHeight(head.left), getHeight(head.right)); } } /** * pass head node in list and height of the tree * * @param levelNodes * @param level */ private void printTree(List<TreeNode> levelNodes, int level) { List<TreeNode> nodes = new ArrayList<TreeNode>(); //indentation for first node in given level printIndentForLevel(level); for (TreeNode treeNode : levelNodes) { //print node data System.out.print(treeNode == null?" ":treeNode.data); //spacing between nodes printSpacingBetweenNodes(level); //if its not a leaf node if(level>1){ nodes.add(treeNode == null? null:treeNode.left); nodes.add(treeNode == null? null:treeNode.right); } } System.out.println(); if(level>1){ printTree(nodes, level-1); } } private void printIndentForLevel(int level){ for (int i = (int) (Math.pow(2,level-1)); i >0; i--) { System.out.print(" "); } } private void printSpacingBetweenNodes(int level){ //spacing between nodes for (int i = (int) ((Math.pow(2,level-1))*2)-1; i >0; i--) { System.out.print(" "); } } Prints Tree in following format: 4 3 7 1 5 8 2 10 9
Scala语言的解决scheme,类似于我在java中写的 :
case class Node(name: String, children: Node*) { def toTree: String = toTree("", "").mkString("\n") private def toTree(prefix: String, childrenPrefix: String): Seq[String] = { val firstLine = prefix + this.name val firstChildren = this.children.dropRight(1).flatMap { child => child.toTree(childrenPrefix + "├── ", childrenPrefix + "│ ") } val lastChild = this.children.takeRight(1).flatMap { child => child.toTree(childrenPrefix + "└── ", childrenPrefix + " ") } firstLine +: firstChildren ++: lastChild } }
输出示例:
vasya ├── frosya │ ├── petya │ │ └── masha │ └── kolya └── frosya2
与java解决scheme相比,它没有不必要的基础缩进,并且更好地inheritance了String
-sa( StringBuilder
)。 它仍然可以导致深层嵌套树的StackOverflowexception。 改善的空间; 😉
我需要在我的一个项目中打印一个二叉树,为此我准备了一个java类TreePrinter
,其中一个示例输出是:
[+] / \ / \ / \ / \ / \ [*] \ / \ [-] [speed] [2] / \ [45] [12]
[+] / \ / \ / \ / \ / \ [*] \ / \ [-] [speed] [2] / \ [45] [12]
这里是TreePrinter
类和TextNode
类的TextNode
。 对于打印任何树,您可以创build一个TextNode
类的等价树。
import java.util.ArrayList; public class TreePrinter { public TreePrinter(){ } public static String TreeString(TextNode root){ ArrayList layers = new ArrayList(); ArrayList bottom = new ArrayList(); FillBottom(bottom, root); DrawEdges(root); int height = GetHeight(root); for(int i = 0; i s.length()) min = s.length(); if(!n.isEdge) s += "["; s += n.text; if(!n.isEdge) s += "]"; layers.set(n.depth, s); } StringBuilder sb = new StringBuilder(); for(int i = 0; i temp = new ArrayList(); for(int i = 0; i 0) temp.get(i-1).left = x; temp.add(x); } temp.get(count-1).left = n.left; n.left.depth = temp.get(count-1).depth+1; n.left = temp.get(0); DrawEdges(temp.get(count-1).left); } if(n.right != null){ int count = n.right.x - (nx + n.text.length() + 2); ArrayList temp = new ArrayList(); for(int i = 0; i 0) temp.get(i-1).right = x; temp.add(x); } temp.get(count-1).right = n.right; n.right.depth = temp.get(count-1).depth+1; n.right = temp.get(0); DrawEdges(temp.get(count-1).right); } } private static void FillBottom(ArrayList bottom, TextNode n){ if(n == null) return; FillBottom(bottom, n.left); if(!bottom.isEmpty()){ int i = bottom.size()-1; while(bottom.get(i).isEdge) i--; TextNode last = bottom.get(i); if(!n.isEdge) nx = last.x + last.text.length() + 3; } bottom.add(n); FillBottom(bottom, n.right); } private static boolean isLeaf(TextNode n){ return (n.left == null && n.right == null); } private static int GetHeight(TextNode n){ if(n == null) return 0; int l = GetHeight(n.left); int r = GetHeight(n.right); return Math.max(l, r) + 1; } } class TextNode { public String text; public TextNode parent, left, right; public boolean isEdge; public int x, depth; public TextNode(String text){ this.text = text; parent = null; left = null; right = null; isEdge = false; x = 0; depth = 0; } }
import java.util.ArrayList; public class TreePrinter { public TreePrinter(){ } public static String TreeString(TextNode root){ ArrayList layers = new ArrayList(); ArrayList bottom = new ArrayList(); FillBottom(bottom, root); DrawEdges(root); int height = GetHeight(root); for(int i = 0; i s.length()) min = s.length(); if(!n.isEdge) s += "["; s += n.text; if(!n.isEdge) s += "]"; layers.set(n.depth, s); } StringBuilder sb = new StringBuilder(); for(int i = 0; i temp = new ArrayList(); for(int i = 0; i 0) temp.get(i-1).left = x; temp.add(x); } temp.get(count-1).left = n.left; n.left.depth = temp.get(count-1).depth+1; n.left = temp.get(0); DrawEdges(temp.get(count-1).left); } if(n.right != null){ int count = n.right.x - (nx + n.text.length() + 2); ArrayList temp = new ArrayList(); for(int i = 0; i 0) temp.get(i-1).right = x; temp.add(x); } temp.get(count-1).right = n.right; n.right.depth = temp.get(count-1).depth+1; n.right = temp.get(0); DrawEdges(temp.get(count-1).right); } } private static void FillBottom(ArrayList bottom, TextNode n){ if(n == null) return; FillBottom(bottom, n.left); if(!bottom.isEmpty()){ int i = bottom.size()-1; while(bottom.get(i).isEdge) i--; TextNode last = bottom.get(i); if(!n.isEdge) nx = last.x + last.text.length() + 3; } bottom.add(n); FillBottom(bottom, n.right); } private static boolean isLeaf(TextNode n){ return (n.left == null && n.right == null); } private static int GetHeight(TextNode n){ if(n == null) return 0; int l = GetHeight(n.left); int r = GetHeight(n.right); return Math.max(l, r) + 1; } } class TextNode { public String text; public TextNode parent, left, right; public boolean isEdge; public int x, depth; public TextNode(String text){ this.text = text; parent = null; left = null; right = null; isEdge = false; x = 0; depth = 0; } }
最后这里是打印给定示例的testing类:
public class Test { public static void main(String[] args){ TextNode root = new TextNode("+"); root.left = new TextNode("*"); root.left.parent = root; root.right = new TextNode("-"); root.right.parent = root; root.left.left = new TextNode("speed"); root.left.left.parent = root.left; root.left.right = new TextNode("2"); root.left.right.parent = root.left; root.right.left = new TextNode("45"); root.right.left.parent = root.right; root.right.right = new TextNode("12"); root.right.right.parent = root.right; System.out.println(TreePrinter.TreeString(root)); } }
这是一个打印出树的非常简单的解决scheme。 这不是那么漂亮,但它非常简单:
enum { kWidth = 6 }; void PrintSpace(int n) { for (int i = 0; i < n; ++i) printf(" "); } void PrintTree(struct Node * root, int level) { if (!root) return; PrintTree(root->right, level + 1); PrintSpace(level * kWidth); printf("%d", root->data); PrintTree(root->left, level + 1); }
示例输出:
106 105 104 103 102 101 100
你可以使用一个小程序来很容易地看到它。 您需要打印下列项目。
-
将节点打印为具有可见半径的圆形
-
获取每个节点的坐标。
-
可以将x坐标可视化为在其中序遍历中访问节点之前访问的节点的数量。
-
y坐标可以被视为特定节点的深度。
-
-
打印父母和子女之间的界限
-
这可以通过将节点的x和y坐标以及每个节点的父节点保持在单独的列表中来完成。
-
对于除root之外的每个节点,通过获取子节点和父节点的x和y坐标,将每个节点与其父节点连接起来。
-
我知道你们都有很好的解决办法; 我只是想分享我的 – 也许这不是最好的方式,但它是完美的我自己!
用python
和pip
,这真的很简单! 繁荣!
在Mac或Ubuntu(我的Mac)
- 开放的terminal
-
$ pip install drawtree
-
$python
,inputpython console; 你可以用其他方式做 -
from drawtree import draw_level_order
-
draw_level_order('{2,1,3,0,7,9,1,2,#,1,0,#,#,8,8,#,#,#,#,7}')
DONE!
2 / \ / \ / \ 1 3 / \ / \ 0 7 9 1 / / \ / \ 2 1 0 8 8 / 7
源跟踪:
在我看到这篇文章之前,我去了谷歌“二叉树纯文本”
我发现这个https://www.reddit.com/r/learnpython/comments/3naiq8/draw_binary_tree_in_plain_text/ ,指示我到这个https://github.com/msbanik/drawtree
我发现VasyaNovikov的答案对于打印一个大的通用树非常有用,并将其修改为二叉树
码:
class TreeNode { Integer data = null; TreeNode left = null; TreeNode right = null; TreeNode(Integer data) {this.data = data;} public void print() { print("", this, false); } public void print(String prefix, TreeNode n, boolean isLeft) { if (n != null) { System.out.println (prefix + (isLeft ? "|-- " : "\\-- ") + n.data); print(prefix + (isLeft ? "| " : " "), n.left, true); print(prefix + (isLeft ? "| " : " "), n.right, false); } } }
示例输出:
\-- 7 |-- 3 | |-- 1 | | \-- 2 | \-- 5 | |-- 4 | \-- 6 \-- 11 |-- 9 | |-- 8 | \-- 10 \-- 13 |-- 12 \-- 14
private StringBuilder prettyPrint(Node root, int currentHeight, int totalHeight) { StringBuilder sb = new StringBuilder(); int spaces = getSpaceCount(totalHeight-currentHeight + 1); if(root == null) { //create a 'spatial' block and return it String row = String.format("%"+(2*spaces+1)+"s%n", ""); //now repeat this row space+1 times String block = new String(new char[spaces+1]).replace("\0", row); return new StringBuilder(block); } if(currentHeight==totalHeight) return new StringBuilder(root.data+""); int slashes = getSlashCount(totalHeight-currentHeight +1); sb.append(String.format("%"+(spaces+1)+"s%"+spaces+"s", root.data+"", "")); sb.append("\n"); //now print / and \ // but make sure that left and right exists char leftSlash = root.left == null? ' ':'/'; char rightSlash = root.right==null? ' ':'\\'; int spaceInBetween = 1; for(int i=0, space = spaces-1; i<slashes; i++, space --, spaceInBetween+=2) { for(int j=0; j<space; j++) sb.append(" "); sb.append(leftSlash); for(int j=0; j<spaceInBetween; j++) sb.append(" "); sb.append(rightSlash+""); for(int j=0; j<space; j++) sb.append(" "); sb.append("\n"); } //sb.append("\n"); //now get string representations of left and right subtrees StringBuilder leftTree = prettyPrint(root.left, currentHeight+1, totalHeight); StringBuilder rightTree = prettyPrint(root.right, currentHeight+1, totalHeight); // now line by line print the trees side by side Scanner leftScanner = new Scanner(leftTree.toString()); Scanner rightScanner = new Scanner(rightTree.toString()); // spaceInBetween+=1; while(leftScanner.hasNextLine()) { if(currentHeight==totalHeight-1) { sb.append(String.format("%-2s %2s", leftScanner.nextLine(), rightScanner.nextLine())); sb.append("\n"); spaceInBetween-=2; } else { sb.append(leftScanner.nextLine()); sb.append(" "); sb.append(rightScanner.nextLine()+"\n"); } } return sb; } private int getSpaceCount(int height) { return (int) (3*Math.pow(2, height-2)-1); } private int getSlashCount(int height) { if(height <= 3) return height -1; return (int) (3*Math.pow(2, height-3)-1); }
https://github.com/murtraja/java-binary-tree-printer
只适用于1到2位整数(我懒得使它通用)
在控制台中打印:
500 700 300 200 400
简单的代码:
public int getHeight() { if(rootNode == null) return -1; return getHeight(rootNode); } private int getHeight(Node node) { if(node == null) return -1; return Math.max(getHeight(node.left), getHeight(node.right)) + 1; } public void printBinaryTree(Node rootNode) { Queue<Node> rootsQueue = new LinkedList<Node>(); Queue<Node> levelQueue = new LinkedList<Node>(); levelQueue.add(rootNode); int treeHeight = getHeight(); int firstNodeGap; int internalNodeGap; int copyinternalNodeGap; while(true) { System.out.println(""); internalNodeGap = (int)(Math.pow(2, treeHeight + 1) -1); copyinternalNodeGap = internalNodeGap; firstNodeGap = internalNodeGap/2; boolean levelFirstNode = true; while(!levelQueue.isEmpty()) { internalNodeGap = copyinternalNodeGap; Node currNode = levelQueue.poll(); if(currNode != null) { if(levelFirstNode) { while(firstNodeGap > 0) { System.out.format("%s", " "); firstNodeGap--; } levelFirstNode =false; } else { while(internalNodeGap>0) { internalNodeGap--; System.out.format("%s", " "); } } System.out.format("%3d",currNode.data); rootsQueue.add(currNode); } } --treeHeight; while(!rootsQueue.isEmpty()) { Node currNode = rootsQueue.poll(); if(currNode != null) { levelQueue.add(currNode.left); levelQueue.add(currNode.right); } } if(levelQueue.isEmpty()) break; } }
这是一个非常灵活的树型打印机。 不是最好看,但它处理了很多案件。 随意添加斜线,如果你能弄清楚。
package com.tomac120.NodePrinter; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * Created by elijah on 6/28/16. */ public class NodePrinter{ final private List<List<PrintableNodePosition>> nodesByRow; int maxColumnsLeft = 0; int maxColumnsRight = 0; int maxTitleLength = 0; String sep = " "; int depth = 0; public NodePrinter(PrintableNode rootNode, int chars_per_node){ this.setDepth(rootNode,1); nodesByRow = new ArrayList<>(depth); this.addNode(rootNode._getPrintableNodeInfo(),0,0); for (int i = 0;i<chars_per_node;i++){ //sep += " "; } } private void setDepth(PrintableNode info, int depth){ if (depth > this.depth){ this.depth = depth; } if (info._getLeftChild() != null){ this.setDepth(info._getLeftChild(),depth+1); } if (info._getRightChild() != null){ this.setDepth(info._getRightChild(),depth+1); } } private void addNode(PrintableNodeInfo node, int level, int position){ if (position < 0 && -position > maxColumnsLeft){ maxColumnsLeft = -position; } if (position > 0 && position > maxColumnsRight){ maxColumnsRight = position; } if (node.getTitleLength() > maxTitleLength){ maxTitleLength = node.getTitleLength(); } List<PrintableNodePosition> row = this.getRow(level); row.add(new PrintableNodePosition(node, level, position)); level++; int depthToUse = Math.min(depth,6); int levelToUse = Math.min(level,6); int offset = depthToUse - levelToUse-1; offset = (int)(Math.pow(offset,Math.log(depthToUse)*1.4)); offset = Math.max(offset,3); PrintableNodeInfo leftChild = node.getLeftChildInfo(); PrintableNodeInfo rightChild = node.getRightChildInfo(); if (leftChild != null){ this.addNode(leftChild,level,position-offset); } if (rightChild != null){ this.addNode(rightChild,level,position+offset); } } private List<PrintableNodePosition> getRow(int row){ if (row > nodesByRow.size() - 1){ nodesByRow.add(new LinkedList<>()); } return nodesByRow.get(row); } public void print(){ int max_chars = this.maxColumnsLeft+maxColumnsRight+1; int level = 0; String node_format = "%-"+this.maxTitleLength+"s"; for (List<PrintableNodePosition> pos_arr : this.nodesByRow){ String[] chars = this.getCharactersArray(pos_arr,max_chars); String line = ""; int empty_chars = 0; for (int i=0;i<chars.length+1;i++){ String value_i = i < chars.length ? chars[i]:null; if (chars.length + 1 == i || value_i != null){ if (empty_chars > 0) { System.out.print(String.format("%-" + empty_chars + "s", " ")); } if (value_i != null){ System.out.print(String.format(node_format,value_i)); empty_chars = -1; } else{ empty_chars = 0; } } else { empty_chars++; } } System.out.print("\n"); int depthToUse = Math.min(6,depth); int line_offset = depthToUse - level; line_offset *= 0.5; line_offset = Math.max(0,line_offset); for (int i=0;i<line_offset;i++){ System.out.println(""); } level++; } } private String[] getCharactersArray(List<PrintableNodePosition> nodes, int max_chars){ String[] positions = new String[max_chars+1]; for (PrintableNodePosition a : nodes){ int pos_i = maxColumnsLeft + a.column; String title_i = a.nodeInfo.getTitleFormatted(this.maxTitleLength); positions[pos_i] = title_i; } return positions; } }
NodeInfo class
package com.tomac120.NodePrinter; /** * Created by elijah on 6/28/16. */ public class PrintableNodeInfo { public enum CLI_PRINT_COLOR { RESET("\u001B[0m"), BLACK("\u001B[30m"), RED("\u001B[31m"), GREEN("\u001B[32m"), YELLOW("\u001B[33m"), BLUE("\u001B[34m"), PURPLE("\u001B[35m"), CYAN("\u001B[36m"), WHITE("\u001B[37m"); final String value; CLI_PRINT_COLOR(String value){ this.value = value; } @Override public String toString() { return value; } } private final String title; private final PrintableNode leftChild; private final PrintableNode rightChild; private final CLI_PRINT_COLOR textColor; public PrintableNodeInfo(String title, PrintableNode leftChild, PrintableNode rightChild){ this(title,leftChild,rightChild,CLI_PRINT_COLOR.BLACK); } public PrintableNodeInfo(String title, PrintableNode leftChild, PrintableNode righthild, CLI_PRINT_COLOR textColor){ this.title = title; this.leftChild = leftChild; this.rightChild = righthild; this.textColor = textColor; } public String getTitle(){ return title; } public CLI_PRINT_COLOR getTextColor(){ return textColor; } public String getTitleFormatted(int max_chars){ return this.textColor+title+CLI_PRINT_COLOR.RESET; /* String title = this.title.length() > max_chars ? this.title.substring(0,max_chars+1):this.title; boolean left = true; while(title.length() < max_chars){ if (left){ title = " "+title; } else { title = title + " "; } } return this.textColor+title+CLI_PRINT_COLOR.RESET;*/ } public int getTitleLength(){ return title.length(); } public PrintableNodeInfo getLeftChildInfo(){ if (leftChild == null){ return null; } return leftChild._getPrintableNodeInfo(); } public PrintableNodeInfo getRightChildInfo(){ if (rightChild == null){ return null; } return rightChild._getPrintableNodeInfo(); } }
NodePosition class
package com.tomac120.NodePrinter; /** * Created by elijah on 6/28/16. */ public class PrintableNodePosition implements Comparable<PrintableNodePosition> { public final int row; public final int column; public final PrintableNodeInfo nodeInfo; public PrintableNodePosition(PrintableNodeInfo nodeInfo, int row, int column){ this.row = row; this.column = column; this.nodeInfo = nodeInfo; } @Override public int compareTo(PrintableNodePosition o) { return Integer.compare(this.column,o.column); } }
And, finally, Node Interface
package com.tomac120.NodePrinter; /** * Created by elijah on 6/28/16. */ public interface PrintableNode { PrintableNodeInfo _getPrintableNodeInfo(); PrintableNode _getLeftChild(); PrintableNode _getRightChild(); }
C++:
struct node{ string key; struct node *left, *right; }; void printBFS(struct node *root){ std::queue<struct node *> q; q.push(root); while(q.size() > 0){ int levelNodes = q.size(); while(levelNodes > 0){ struct node *p = q.front(); q.pop(); cout << " " << p->key ; if(p->left != NULL) q.push(p->left); if(p->right != NULL) q.push(p->right); levelNodes--; } cout << endl; } }
Input :
Balanced tree created from:
string a[] = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n"};
输出:
gckaeimbdfhjln
Algorithm:
- Create a ArrayList of Linked List Nodes.
- Do the level order traversal using queue(Breadth First Search).
- For getting all the nodes at each level, before you take out a node from queue, store the size of the queue in a variable, say you call it as levelNodes.
- Now while levelNodes > 0, take out the nodes and print it and add their children into the queue.
- After this while loop put a line break.
A Scala solution, adapted from Vasya Novikov's answer and specialized for binary trees:
/** An immutable Binary Tree. */ case class BTree[T](value: T, left: Option[BTree[T]], right: Option[BTree[T]]) { /* Adapted from: http://stackoverflow.com/a/8948691/643684 */ def pretty: String = { def work(tree: BTree[T], prefix: String, isTail: Boolean): String = { val (line, bar) = if (isTail) ("└── ", " ") else ("├── ", "│") val curr = s"${prefix}${line}${tree.value}" val rights = tree.right match { case None => s"${prefix}${bar} ├── ∅" case Some(r) => work(r, s"${prefix}${bar} ", false) } val lefts = tree.left match { case None => s"${prefix}${bar} └── ∅" case Some(l) => work(l, s"${prefix}${bar} ", true) } s"${curr}\n${rights}\n${lefts}" } work(this, "", true) } }
See also these answers .
In particular it wasn't too difficult to use abego TreeLayout to produce results shown below with the default settings.
If you try that tool, note this caveat: It prints children in the order they were added. For a BST where left vs right matters I found this library to be inappropriate without modification.
Also, the method to add children simply takes a parent
and child
node as parameters. (So to process a bunch of nodes, you must take the first one separately to create a root.)
I ended up using this solution above, modifying it to take in the type <Node>
so as to have access to Node
's left and right (children).