我需要帮助来理解如何为任何给定的二叉树编写漂亮的打印机。
我知道第一步是预先订购以获取所有节点。
我知道在前序遍历中,这就是所有漂亮的实现。
只是不确定如何开始。我有一个前序遍历功能,但我不知道从哪里开始修改它,或者我应该自己做功能。
没有代码,只是询问有关别人如何处理它的想法。
如果我绝望的话可能是后来的代码:P
具体来说,它应该是这样的:
答案 0 :(得分:9)
如果您知道树的深度,即水平数,您可以计算最后一级中的最大节点数(n 2 ,n =级别数 - 1,1元素,如果你只有根)。如果您进一步知道元素的宽度(例如,如果您最多有2位数字,则每个元素的宽度为2),您可以计算最后一个级别的宽度:
((number of elements - 1) * (element width + spacing) + element width)
。击>
实际上上段并不重要。您只需要树的深度,即要显示的最大级别。但是,如果树是稀疏的,即不存在最后一级和上面的所有元素,则需要获取正在渲染的节点的位置,以便相应地调整该情况的缩进/间距。
在预订迭代中,您可以计算每个级别的元素之间的缩进和间距。缩进的公式为: 2 (最高级别 - 级别) - 1 和间距: 2 (最高级别 - 级别+ 1 ) - 1 (级别为1)
示例:
1
2 3
4 5 6 7
8 9 A B C D E F
在该树中,级别数为4.最后一级的间距为1,而缩进为0.您将获得以下值:
indent = 7
:( 2 (4-1) - 1 = 2 3 - 1 = 8 - 1 = 7)indent = 3
:( 2 (4-2) - 1 = 2 2 - 1 = 4 - 1 = 3)spacing = 7
(2 (4-1) - 1 = 2 3 - 1 = 8 - 1 = 7)indent = 1
:( 2 (4-3) - 1 = 2 1 - 1 = 2 - 1 = 1)spacing = 3
(2 (4-2) - 1 = 2 2 - 1 = 4 - 1 = 3)indent = 0
:( 2 (4-4) - 1 = 2 0 - 1 = 1 - 1 = 0)spacing = 1
:( 2 (4-3) - 1 = 2 1 - 1 = 2 - 1 = 1)请注意,在最后一级,您将始终具有间距1 *元素宽度。因此,对于最大元素宽度为3(例如3位数字),您在最后一级的间距为3,以便获得较高级别的一些非常对齐。
编辑:对于漂亮的打印,您只需将缩进计算为width element * level
,其中级别将从零开始。然后,如果节点不是叶子,则在绘制子项后使用前面的开头paranthesis和一个闭合的paranthesis绘制它,如果它是一个叶子只是绘制它并且如果缺少叶子,则绘制双重paranthis。
因此你会得到这样的东西:
public void printSubtree( int indent, node ) {
for( int i = 0; i < indent; ++i) {
System.out.print(" ");
}
if( inner node) {
System.out.println("(" + value);
printSubtree(indent + elem width, left child); //this is a recursive call, alternatively use the indent formula above if you don't use recursion
printSubtree(indent + elem width, right child);
//we have a new line so print the indent again
for( int i = 0; i < indent; ++i) {
System.out.print(" ");
}
System.out.println(")");
} else if( not empty) {
System.out.println(value);
} else { //empty/non existing node
System.out.println("()");
}
}
答案 1 :(得分:5)
package com.sai.samples;
/**
* @author Saiteja Tokala
*/
import java.util.ArrayList;
import java.util.List;
/**
* Binary tree printer
*
* @author saiteja
*/
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;
}
}
}
答案 2 :(得分:1)
由于遍历函数是递归的,因此您可以将格式化参数(例如缩进每个节点的空格数)传递给递归调用。例如。如果要为树中的每个级别缩进2个空格,可以使用参数0开始递归,然后在每个递归步骤中添加2。
答案 3 :(得分:0)
假设您的二叉树以下列方式表示:
public class BinaryTreeModel {
private Object value;
private BinaryTreeModel left;
private BinaryTreeModel right;
public BinaryTreeModel(Object value) {
this.value = value;
}
// standard getters and setters
}
那么漂亮打印树的代码可以这样写:
public String traversePreOrder(BinaryTreeModel root) {
if (root == null) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append(root.getValue());
String pointerRight = "└──";
String pointerLeft = (root.getRight() != null) ? "├──" : "└──";
traverseNodes(sb, "", pointerLeft, root.getLeft(), root.getRight() != null);
traverseNodes(sb, "", pointerRight, root.getRight(), false);
return sb.toString();
}
public void traverseNodes(StringBuilder sb, String padding, String pointer, BinaryTreeModel node,
boolean hasRightSibling) {
if (node != null) {
sb.append("\n");
sb.append(padding);
sb.append(pointer);
sb.append(node.getValue());
StringBuilder paddingBuilder = new StringBuilder(padding);
if (hasRightSibling) {
paddingBuilder.append("│ ");
} else {
paddingBuilder.append(" ");
}
String paddingForBoth = paddingBuilder.toString();
String pointerRight = "└──";
String pointerLeft = (node.getRight() != null) ? "├──" : "└──";
traverseNodes(sb, paddingForBoth, pointerLeft, node.getLeft(), node.getRight() != null);
traverseNodes(sb, paddingForBoth, pointerRight, node.getRight(), false);
}
}
然后你需要做的就是像这样调用根节点的函数:
System.out.println(traversePreOrder(root));
本文详细解释了整个方法 - https://www.baeldung.com/java-print-binary-tree-diagram。这个想法可以扩展到漂亮的打印 generic trees
、tries
等等。
假设 trie 的辅助类是 Trie
和 TrieNode
,定义如下:
public class TrieNode{
public HashMap<Character, TrieNode> children;
public boolean isWord;
public TrieNode() {
children = new HashMap<>();
isWord = false;
}
}
和
public class Trie{
public TrieNode root;
public Trie() {
root = new TrieNode();
}
// Insert method for trie, search method for trie, delete method for trie not shown as they are not relevant
}
现在,要实现漂亮的打印方法,只需在 Trie
类中插入两个方法:
public String traversePreOrder() {
if (root == null) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append("○");
Set<Character> childNodeChars = root.children.keySet();
Iterator<Character> iterator = childNodeChars.iterator();
while (iterator.hasNext()) {
Character childC = iterator.next();
if (!iterator.hasNext())
traverseNodes(sb, "", "└──", root, childC, false);
else
traverseNodes(sb, "", "├──", root, childC, true);
}
return sb.toString();
}
private void traverseNodes(StringBuilder sb, String padding, String pointer, TrieNode node, Character c, boolean hasNextSibling) {
sb.append("\n");
sb.append(padding);
sb.append(pointer);
sb.append(c);
sb.append(node.children.get(c).isWord?"(1)":"");
StringBuilder paddingBuilder = new StringBuilder(padding);
paddingBuilder.append(hasNextSibling?"│ ":" ");
String paddingForBoth = paddingBuilder.toString();
TrieNode childNode = node.children.get(c);
if(childNode == null)
return;
Set<Character> childNodeChars = childNode.children.keySet();
Iterator<Character> iterator = childNodeChars.iterator();
while (iterator.hasNext()) {
Character childC = iterator.next();
if (!iterator.hasNext())
traverseNodes(sb, paddingForBoth, "└──", childNode, childC, false);
else
traverseNodes(sb, paddingForBoth, "├──", childNode, childC, true);
}
}