首页 文章

如何打印二叉树图?

提问于
浏览
135

如何在Java中打印二叉树,以便输出如下:

4 
  / \ 
 2   5

我的节点:

public class Node<A extends Comparable> {
    Node<A> left, right;
    A data;

    public Node(A data){
        this.data = data;
    }
}

22 回答

  • 25

    我已经创建了简单的二叉树打印机 . 您可以根据需要使用和修改它,但无论如何它都没有进行优化 . 我认为这里可以改进很多东西;)

    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
    
  • 3

    按行打印[大]树 .

    输出示例:

    └── 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);
            }
        }
    }
    

    附:对不起,这个答案并不完全集中在“二元”树上 . 在请求打印树时,它只是用Google搜索 . 解决方案的灵感来自linux中的“tree”命令 .

  • 0
    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
    

    输入

    8 4 12 2 6 10 14 1 3 5 7 9 11 13 20 15

    这是@ anurag的答案的一个变种 - 看到额外的问题让我烦恼

  • 3

    我为此做了一个改进的算法,它可以很好地处理不同大小的节点 . 它使用线条自上而下打印 .

    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
    
  • 0

    改编自Vasya Novikovanswer以使其更加二进制,并使用 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
    
  • 1

    michal.kreuzman很好,我将不得不说 . 当我发现它真的帮助了我时,我自己做了一个程序并在网上搜索代码时感到很懒 . 但我害怕看到它只适用于单个数字,就像你要使用多个数字一样,因为你使用的是空格而不是制表符,结构会被错放,程序将无法使用它 . 至于我后来的代码,我需要一些更大的输入(至少超过10个)这对我不起作用,并且在我没有找到任何东西后在网上搜索了很多,我自己做了一个程序 . 它现在有一些错误,现在我再次感觉懒得纠正它们但它打印得非常漂亮,节点可以承担任何大的 Value .

    树不会像提到的问题那样,但它旋转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
    

    唯一的问题是扩展分支我会尽快解决问题,但在此之前你也可以使用它 .

  • 242

    你的树每层需要两倍的距离:

    a
          / \
         /   \
        /     \
       /       \
       b       c
      / \     / \
     /   \   /   \
     d   e   f   g
    / \ / \ / \ / \
    h i j k l m n o
    

    您可以将树保存在一个数组数组中,每个深度都有一个数组:

    [[a],[b,c],[d,e,f,g],[h,i,j,k,l,m,n,o]]
    

    如果树未满,则需要在该数组中包含空值:

    a
          / \
         /   \
        /     \
       /       \
       b       c
      / \     / \
     /   \   /   \
     d   e   f   g
    / \   \ / \   \
    h i   k l m   o
    [[a],[b,c],[d,e,f,g],[h,i, ,k,l,m, ,o]]
    

    然后,您可以遍历数组以打印树,在第一个元素之前打印空间,在元素之间打印空间,具体取决于深度并打印行,具体取决于是否填充了下一个图层的数组中的相应元素 . 如果您的值可以超过一个字符长,则需要在创建数组表示时找到最长值,并相应地乘以所有宽度和行数 .

  • 33

    我发现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
    
  • 14
    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
    
  • 34

    Scala 语言的解决方案,类似于I wrote in 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解决方案相比,它没有不必要的基本缩进,并且更好地连接 String -s(引擎盖下的 StringBuilder ) . 它仍然可以导致深度嵌套树的StackOverflow异常 . 改进的空间.--)

  • 2

    这是一个打印出树的非常简单的解决方案 . 它不是那么漂亮,但它非常简单:

    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
    
  • 197

    我知道你们都有很好的解决方案;我只想分享我的 - 也许这不是最好的方式,但它对我自己来说是完美的!

    有了 pythonpip ,它真的很简单!繁荣!

    在Mac或Ubuntu上(我的是mac)

    • 开放终端

    • $ pip install drawtree

    • $python ,进入python控制台;你可以用其他方式做到这一点

    • 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

  • 1

    我需要在我的一个项目中打印一个二叉树,因为我准备了一个java类 TreePrinter ,其中一个示例输出是:

    [+]
                   /   \
                  /     \
                 /       \
                /         \
               /           \
            [*]             \
           /   \             [-]
    [speed]     [2]         /   \
                        [45]     [12]
    

    这是类 TreePrinter 的代码以及类 TextNode . 要打印任何树,您只需使用 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 - (n.x + 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) n.x = 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;
        }
    }
    

    最后,这是一个用于打印给定样本的测试类:

    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));
        }
    }
    
  • 1

    您可以使用applet轻松地将其可视化 . 您需要打印以下项目 .

    • 将节点打印为具有一些可见半径的圆

    • 获取每个节点的坐标 .

    • 可以将x坐标可视化为节点数在其顺序遍历中访问节点之前访问过 .

    • 可以将y坐标可视化为特定节点的深度 .


    • 打印父级和子级之间的行

    • 这可以通过将节点的x和y坐标以及每个节点的父节点保持在单独的列表中来完成 .

    • 对于除root之外的每个节点,通过获取子节点和父节点的x和y坐标,将每个节点与其父节点连接起来 .

  • 0
    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位数的整数(我懒得把它变成通用的)

    skewed

    full

  • 13

    在控制台中打印:

    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;
            }
    
        }
    
  • 0

    这是一款功能多样的树形打印机 . 不是最好看,但它处理了很多情况 . 如果你能搞清楚,可以随意添加斜线 .
    enter image description here

    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类

    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类

    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);
        }
    }
    

    最后,节点接口

    package com.tomac120.NodePrinter;
    
    /**
     * Created by elijah on 6/28/16.
     */
    public interface PrintableNode {
        PrintableNodeInfo _getPrintableNodeInfo();
        PrintableNode _getLeftChild();
        PrintableNode _getRightChild();
    }
    
  • 2

    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 :

    balancer 树创建自:

    string a[] = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n"};
    

    Output:

    g 
     c k 
     a e i m 
     b d f h j l n
    

    Algorithm:

    • 创建链接列表节点的ArrayList .

    • 使用队列(广度优先搜索)进行级别顺序遍历 .

    • 为了获取每个级别的所有节点,在从队列中取出节点之前,将队列的大小存储在变量中,比如将其称为levelNodes .

    • 现在当levelNodes> 0时,取出节点并打印它们并将它们的子节点添加到队列中 .

    • 在此循环之后放置一个换行符 .

  • 0

    Scala解决方案,改编自Vasya Novikov的答案,专门用于二叉树:

    /** 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)
      }
    }
    
  • 0

    另见these answers .

    特别是使用abego TreeLayout以默认设置生成下面显示的结果并不太难 .

    如果您尝试使用该工具,请注意以下警告:它按照添加顺序打印子项 . 对于左右对比的BST,我发现这个库不合适而没有修改 .

    此外,添加子项的方法只需要 parentchild 节点作为参数 . (因此,要处理一堆节点,必须分别使用第一个节点来创建根节点 . )

    我最终使用上面的this solution,修改它以接受类型 <Node> ,以便能够访问 Node 的左和右(儿童) .

    tree created with abego TreeLayout

  • 0

    这是另一种可视化树的方法:将节点保存为xml文件,然后让浏览器显示层次结构:

    class treeNode{
        int key;
        treeNode left;
        treeNode right;
    
        public treeNode(int key){
            this.key = key;
            left = right = null;
        }
    
        public void printNode(StringBuilder output, String dir){
            output.append("<node key='" + key + "' dir='" + dir + "'>");
            if(left != null)
                left.printNode(output, "l");
            if(right != null)
                right.printNode(output, "r");
            output.append("</node>");
        }
    }
    
    class tree{
        private treeNode treeRoot;
    
        public tree(int key){
            treeRoot = new treeNode(key);
        }
    
        public void insert(int key){
            insert(treeRoot, key);
        }
    
        private treeNode insert(treeNode root, int key){
            if(root == null){
                treeNode child = new treeNode(key);
                return child;
            }
    
            if(key < root.key)
                root.left = insert(root.left, key);
            else if(key > root.key)
                root.right = insert(root.right, key);
    
            return root;
        }
    
        public void saveTreeAsXml(){
            StringBuilder strOutput = new StringBuilder();
            strOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            treeRoot.printNode(strOutput, "root");
            try {
                PrintWriter writer = new PrintWriter("C:/tree.xml", "UTF-8");
                writer.write(strOutput.toString());
                writer.close();
            }
            catch (FileNotFoundException e){
    
            }
            catch(UnsupportedEncodingException e){
    
            }
        }
    }
    

    这是测试它的代码:

    tree t = new tree(1);
        t.insert(10);
        t.insert(5);
        t.insert(4);
        t.insert(20);
        t.insert(40);
        t.insert(30);
        t.insert(80);
        t.insert(60);
        t.insert(50);
    
        t.saveTreeAsXml();
    

    输出看起来像这样:

    enter image description here

  • 4

    根据VasyaNovikov回答 . 改进了一些Java魔术:泛型和功能界面 .

    /**
     * Print a tree structure in a pretty ASCII fromat.
     * @param prefix Currnet previx. Use "" in initial call!
     * @param node The current node. Pass the root node of your tree in initial call.
     * @param getChildrenFunc A {@link Function} that returns the children of a given node.
     * @param isTail Is node the last of its sibblings. Use true in initial call. (This is needed for pretty printing.)
     * @param <T> The type of your nodes. Anything that has a toString can be used.
     */
    private <T> void printTreeRec(String prefix, T node, Function<T, List<T>> getChildrenFunc, boolean isTail) {
        String nodeName = node.toString();
        String nodeConnection = isTail ? "└── " : "├── ";
        log.debug(prefix + nodeConnection + nodeName);
        List<T> children = getChildrenFunc.apply(node);
        for (int i = 0; i < children.size(); i++) {
            String newPrefix = prefix + (isTail ? "    " : "│   ");
            printTreeRec(newPrefix, children.get(i), getChildrenFunc, i == children.size()-1);
        }
    }
    

    初始调用示例:

    Function<ChecksumModel, List<ChecksumModel>> getChildrenFunc = node -> getChildrenOf(node)
    printTreeRec("", rootNode, getChildrenFunc, true);
    

    会输出类似的东西

    └── rootNode
        ├── childNode1
        ├── childNode2
        │   ├── childNode2.1
        │   ├── childNode2.2
        │   └── childNode2.3
        ├── childNode3
        └── childNode4
    

相关问题