【问题标题】:How can you print out a tree in a nicely formatted way?如何以格式良好的方式打印出一棵树?
【发布时间】:2011-05-10 00:02:49
【问题描述】:

在树结构中打印一棵树的最简单方法是什么?比如……

                  some root
              /     |         \
          child1   child2     child 3
           /
      anotherchild               / \
                             yup     another

即使手动格式化也很困难。如何让程序以这种方式打印树?

【问题讨论】:

  • 您应该更改与语言无关的标签,因为有些语言/环境是本机实现的。

标签: language-agnostic data-structures tree


【解决方案1】:

下面的答案是用java写的,但是很简单,可以很容易地转录成其他语言:

public interface Function1<R, T1>
{
    R invoke( T1 argument1 );
}

public interface Procedure1<T1>
{
    void invoke( T1 argument1 );
}

public static <T> void dump( T node, Function1<List<T>,T> breeder,
       Function1<String,T> stringizer, Procedure1<String> emitter )
{
    emitter.invoke( stringizer.invoke( node ) );
    dumpRecursive( node, "", breeder, stringizer, emitter );
}

private static final String[][] PREFIXES = { { " ├─ ", " │  " }, { " └─ ", "    " } };

private static <T> void dumpRecursive( T node, String parentPrefix,
        Function1<List<T>,T> breeder, Function1<String,T> stringizer,
        Procedure1<String> emitter )
{
    for( Iterator<T> iterator = breeder.invoke( node ).iterator(); iterator.hasNext(); )
    {
        T childNode = iterator.next();
        String[] prefixes = PREFIXES[iterator.hasNext()? 0 : 1];
        emitter.invoke( parentPrefix + prefixes[0] + stringizer.invoke( childNode ) );
        dumpRecursive( childNode, parentPrefix + prefixes[1], breeder, stringizer, emitter );
    }
}

它产生以下输出:

Automobile
 ├─ Passenger Vehicle
 │   ├─ Light Passenger Vehicle
 │   │   ├─ Two Wheeled
 │   │   │   ├─ Moped
 │   │   │   ├─ Scooter
 │   │   │   └─ Motorcycle
 │   │   ├─ Three Wheeled
 │   │   └─ Four Wheeled
 │   │       ├─ Car
 │   │       ├─ Station Wagon
 │   │       ├─ Pick-up Truck
 │   │       └─ Sports Utility Vehicle
 │   └─ Heavy Passenger Vehicle
 │       ├─ Bus
 │       │   ├─ Single-Deck Bus
 │       │   │   ├─ Mini Bus
 │       │   │   └─ Big Bus
 │       │   └─ Double-Deck Bus
 │       └─ Coach
 │           ├─ Deluxe
 │           └─ Air-Conditioned
 └─ Goods Vehicle
     ├─ Light Goods Vehicle
     │   ├─ Delivery Van
     │   ├─ Light Truck
     │   └─ Tempo
     │       ├─ Three Wheeler Tempo
     │       └─ Four Wheeler Tempo
     └─ Heavy Goods Vehicle
         ├─ Truck
         └─ Tractor Trailer

...如果您使用以下示例程序调用它:

final class Scratch
{
    static class Node
    {
        String name;
        List<Node> children;

        Node( String name, Node... children )
        {
            this.name = name;
            this.children = Arrays.asList( children );
        }
    }

    public static void main( String[] args )
    {
        Node tree = new Node( "Automobile",
                new Node( "Passenger Vehicle",
                        new Node( "Light Passenger Vehicle",
                                new Node( "Two Wheeled",
                                        new Node( "Moped" ),
                                        new Node( "Scooter" ),
                                        new Node( "Motorcycle" ) ),
                                new Node( "Three Wheeled" ),
                                new Node( "Four Wheeled",
                                        new Node( "Car" ),
                                        new Node( "Station Wagon" ),
                                        new Node( "Pick-up Truck" ),
                                        new Node( "Sports Utility Vehicle" ) ) ),
                        new Node( "Heavy Passenger Vehicle",
                                new Node( "Bus",
                                        new Node( "Single-Deck Bus",
                                                new Node( "Mini Bus" ),
                                                new Node( "Big Bus" ) ),
                                        new Node( "Double-Deck Bus" ) ),
                                new Node( "Coach",
                                        new Node( "Deluxe" ),
                                        new Node( "Air-Conditioned" ) ) ) ),
                new Node( "Goods Vehicle",
                        new Node( "Light Goods Vehicle",
                                new Node( "Delivery Van" ),
                                new Node( "Light Truck" ),
                                new Node( "Tempo",
                                        new Node( "Three Wheeler Tempo" ),
                                        new Node( "Four Wheeler Tempo" ) ) ),
                        new Node( "Heavy Goods Vehicle",
                                new Node( "Truck" ),
                                new Node( "Tractor Trailer" ) ) ) );
        dump( tree, n -> n.children, n -> n.name, s -> System.out.println( s ) );
    }
}

【讨论】:

  • 不错!我曾经写过一个名为text-tree 的小库,它可以在Java 中生成类似的树。
【解决方案2】:

去年我不得不这样做,编写一个家谱应用程序。在网上找到了一个 java 教程,但是我的 Google-fu 今天让我失败了,所以我将不得不简单地解释一下。

它是一种简单的递归算法,根据子节点调整父节点的位置。在伪代码中是这样的:

positionNode (node,x,y) {
    foreach (child in node.children) {
        positionNode(child,x,y+1)
        x ++
    }
    node.x = (x-1)/2
    node.y = y
}

我可能没记错,你可能需要稍微调整一下代码才能正确。

【讨论】:

    【解决方案3】:

    除非有一些不错的图形库可供您使用,否则以您描述的方式表示层次结构会遇到很多麻烦。

    假设您要将其打印到控制台或文件,您将不得不预先计算整个树中所有数据元素的长度,以便正确排列它们。以及如何处理换行之类的事情?

    更好的方法是垂直表示树,使用缩进显示子元素。

    Root
        - Child1
            - Grandchild1
            - Grandchild2
        - Child2
            - Grandchild3
            - Grandchild4
    

    这更容易编码,并且更能容忍换行之类的事情——因为一行中只有一个元素。这就是文件夹浏览器或 xml 文档可能显示其分层数据的方式。

    为此,您需要进行深度优先遍历,然后在递归步骤之前打印出节点:

    public void PrintNode(TreeNode node)
    {
        PrintNode(node, 0);
    }
    
    private void PrintNode(TreeNode node, int indentation)
    {
        // Print the value to the console/file/whatever
        // This prefixes the value with the necessary amount of indentation
        Print(node.Value, indentation);
    
        // Recursively call the child nodes.
        foreach(TreeNode childNode in node.Children)
        {
            PrintNode(childNode, indentation + 1); // Increment the indentation counter.
        }
    }
    

    希望有帮助

    【讨论】:

    • 您肯定会将缩进深度作为第二个参数传递给PrintNode(客户端调用默认为0,并在递归调用中添加一个级别)?这样你就不必手动递减它了。
    • 这对于以请求的格式打印树没有用。
    • @Zecc - 这就是我最后一句话的意思(在我的例子之后)。我试图以与语言无关的方式编写它,而没有指定缩进或打印的实际完成方式。实际上,我通常会进行公开调用,使用 0 调用私有覆盖,以向消费者隐藏它。
    • @Paul Creasey - 你明白了 - 用行表示树比使用列水平表示要容易得多。您正在打印的数据元素的长度可能会导致各种错位。您还会遇到控制台上的列限制问题。我认为答案仍然有用。我什至不会接受尝试按照问题指定的方式打印出来。
    • @Zecc - 好的,我已经添加了你的版本。现在我在看,我认为我原来的简化是不必要的,后一种方法已经足够清楚了。 :-)
    【解决方案4】:

    this answer 来回答类似的问题怎么样? 它打印出漂亮的 ASCII 艺术树。

    或者如果你想完全图形化,也许this one

    【讨论】:

      【解决方案5】:

      虽然我自己没有尝试过,但graphviz 有一个纯文本output format

      【讨论】:

        【解决方案6】:

        好吧,您可以尝试类似 PHP 的 var_dump - 如果您在树状数组上尝试 var_dump,它将为您提供该树的公平表示,即:

        root {
            child1 {
                anotherchild
            }
            child2
            child3 {
                yup
                another
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2013-10-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-22
          • 1970-01-01
          • 2021-05-04
          • 1970-01-01
          相关资源
          最近更新 更多