如何打印二叉树图?

时间:2011-04-09 23:59:23

标签: java formatting binary-tree

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

        cat
        /\
     cat1 cat2

值可以是多个字符。

3 个答案:

答案 0 :(得分:3)

我通常使用graphviz中的点程序。有一个简单的online demo。这样您就不必担心间距或字体宽度了。

答案 1 :(得分:3)

    cat
    /\
 cat1 cat2

二叉树由

组成
  • 根节点
  • 左子树
  • 一个正确的子树

要打印这样一棵树,我们想要打印左右子树(至少有一个空格),然后在它上面打印根节点,以两个子树的中间为中心,并连接到ASCII行。为此,我们需要知道两个子树的宽度。

使用这些想法和递归来创建树形图。


这是一个可能有用的方法规范:

/**
 * creates an ASCII-drawing of a binary tree.
 * @param node the root node of the tree in question
 * @return a String[] of the individual lines of the drawing.
 *    The first line contains the representation of the root node,
 *    the last line only leaf nodes, interim lines may contain
 *    line drawing characters or interior nodes.
 * 
 *    All the contained strings have the same length (are padded
 *    with spaces, where necessary).
 */
String[] drawTree(Node node) {
   ...
}

要输出树,您只需要这样做:

for(String line : drawTree(root)) {
    System.out.println(line);
}

那么,我们如何实现我们的drawTree方法?

  • 它对叶节点(即没有子节点的节点)有什么作用?
  • 如果我们有一个非叶子节点,我们如何将两个这样的调用(对于左右子树),即指定的两个String数组的结果组合成另一个字符串数组,如上所述? (首先看一下两个数组长度相同的简单情况,即树的深度相同。)
祝你好运!

答案 2 :(得分:1)

这是一个完整的,可运行的Demo,它是Scala代码的急速翻译,所以它不是惯用的Java。

有两个实现,一个采用Tree并从中生成一个JTree,另一个使用drawString并使Tree适合JFrame大小。

import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*; 
import javax.swing.JTree;

/**
    (c) GPLv3 2010-09-24
*/
class MNode {

    MNode l; // left
    MNode r; // right
    int t;   // value

    public MNode (int t, MNode l, MNode r) {
        this.l = l;
        this.r = r;
        this.t = t;
    }

    public void add (MNode mn) { 
        if (l == null && t > mn.t) 
            l = mn;
        else if (t > mn.t) 
            l.add (mn);
        else if (r == null) 
            r = mn;
        else    r.add (mn);     
    }
}

abstract class NodePrinter {

    abstract void nodeprint (MNode root);
    int max (int a, int b) { return (a > b) ? a : b; }
    int depth (MNode n) 
    {
        if (n.l == null && n.r == null) return 1;
        if (n.l == null) return 1 + depth (n.r);
        if (n.r == null) return 1 + depth (n.l);
        return 1 + max (depth (n.l), depth (n.r));
    }
}

class SwingPrinter extends NodePrinter {

    void nodeprint (MNode root) {   
        JFrame jf = new JFrame ("Mein Freund, der Baum, ist tot");
        jf.setSize (380, 380);
        jf.setLocationRelativeTo (null);
        JTree jt = new JTree (translate2SwingTree (root));
        jf.add (jt);
        openSubnodes (0, jt);
        jf.setDefaultCloseOperation (WindowConstants.DISPOSE_ON_CLOSE); 
        jf.setVisible (true);
    }

    /**
        Open current branch.
        We need TreePath AND row.
        Open the MNode, iterierate with the row one step, and check there, 
        whether the Branch is a part of the new branch. 
        @param row the row of the starting MNode. 
    */
    void openSubnodes (int row, JTree jt) {
        TreePath tp = jt.getPathForRow (row);
        jt.expandRow (row);
        if (tp.isDescendant (jt.getPathForRow (row + 1)))
            openSubnodes (row + 1, jt);
    }

    DefaultMutableTreeNode translate2SwingTree (MNode ast) 
    {
        DefaultMutableTreeNode dmtn = new DefaultMutableTreeNode ("" + ast.t);
        if (ast.l != null) 
            dmtn.add (translate2SwingTree (ast.l));
        if (ast.r != null) 
            dmtn.add (translate2SwingTree (ast.r));
        return dmtn;
    }   
}

class TreeCanvas extends JPanel {

    private MNode root;
    private NodePrinter np;

    public TreeCanvas (MNode root, NodePrinter np) {
        this.root = root;
        this.np = np;
        d = np.depth (root);
        rows = (2 * d); // - 1
        cols = 2 << d;
    }

    private int d;
    private int rows;
    private int cols;

    // @override 
    public void paint (Graphics g) {
        Dimension dim = getSize ();
        int xf = dim.width / cols;
        int yf = dim.height / rows;
        int fontsize = (xf + yf) / 2;
        g.setFont (g.getFont().deriveFont (fontsize* 1.5f));
        xyPrint (root, dim.width/2, dim.width/2, 1, xf, yf, g);
    }

    /**
        ___50 60 70__________________
      10    |     x0    x0-x1:  (50,30) - (60, 10)  
      20    |    /  \   x0-x2:  (60,10) - (70, 30)
      30    |  x1    x2
    */
    void xyPrint (MNode n, int x, int dx, int y, int xf, int yf, Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setStroke (new BasicStroke (3.0f));

        g.drawString ("" + n.t, x - xf, (y+1) * yf);
        g.setColor (Color.BLACK);
        if (n.l != null) {
            g.drawLine (x - (dx/2) + xf, (y+2) * yf, x, (y+1) * yf); // line:Up
            xyPrint (n.l, x - dx/2, dx/2, y + 2, xf, yf, g);
        }
        if (n.r != null) {
            g.drawLine (x + xf, (y+1) * yf, x + (dx/2), (y+2) * yf); // line down
            xyPrint (n.r, x + dx/2, dx/2, y + 2, xf, yf, g);
        }
    }
}

class ColorSwingPrinter extends NodePrinter {

    void nodeprint (MNode root) {   
        JFrame jf = new JFrame ("Rootnode");
        jf.setSize (650, 520);
        jf.setLocationRelativeTo (null);
        jf.add (new TreeCanvas (root, this));
        jf.setDefaultCloseOperation (WindowConstants.DISPOSE_ON_CLOSE);
        jf.setVisible (true);
    }
}

class RootNode extends MNode {

    public RootNode (String s) 
    {
        super (Integer.parseInt ("" + s.charAt (0)), null, null);
        for (String elem: s.substring (2).split (" "))
        {
            int i = Integer.parseInt (elem);
            MNode mn = new MNode (i, null, null);
            super.add (mn);
        }
    }   
}

public class NodePrinterTest {

    public static void main (String [] args) 
    {
        String param = "6 7 4 3 8 2 9 5";
        /*              6
                     4      7
                3       5       8
            2                       9
        */              
        RootNode root = new RootNode (param);
        ColorSwingPrinter printer = new ColorSwingPrinter ();
        printer.nodeprint (root);
        SwingPrinter printer2 = new SwingPrinter ();
        printer2.nodeprint (root);
    }   
}

打印你的树当然需要一些改编,因为你的节点可能没有公共属性l,r和t,但你应该能够将它们翻译成left()或setLeft()/ getLeft()等等

我不记得ColorSwingPrinter从哪里得到它的名字 - 它应该被命名为ResizingCanvasPrinter或类似的东西。对不起。

以下是两种实现的屏幕截图: enter image description here

需要调整像cat,cat1,cat2这样较长的文本。到现在为止,第一个字符位于后代的中心