C# 如何打印出树结构?

C# 如何打印出树结构?,c#,.net,tree,pretty-print,C#,.net,Tree,Pretty Print,我正在努力提高我们应用程序的性能。我以调用树的形式获得了性能信息,其中包含以下节点类: public class Node { public string Name; // method name public decimal Time; // time spent in method public List<Node> Children; } 一定是这样 A +-B | +-C | +-D | +-E +-F +-G 或者任何类似的东西,只要树结

我正在努力提高我们应用程序的性能。我以调用树的形式获得了性能信息,其中包含以下节点类:

public class Node
{
    public string Name; // method name
    public decimal Time; // time spent in method
    public List<Node> Children;
}
一定是这样

A
+-B
| +-C
| +-D
|   +-E
+-F
  +-G

或者任何类似的东西,只要树结构是可见的。请注意,C和D的缩进方式与G不同-我不能只使用重复的字符串缩进节点。

创建PrintNode方法并使用递归:

class Node
{
    public string Name;
    public decimal Time;
    public List<Node> Children = new List<Node>();

    public void PrintNode(string prefix)
    {
        Console.WriteLine("{0} + {1} : {2}", prefix, this.Name, this.Time);
        foreach (Node n in Children)
            if (Children.IndexOf(n) == Children.Count - 1)
                n.PrintNode(prefix + "    ");
            else
                n.PrintNode(prefix + "   |");
    }
}
在我的例子中,它会给我们类似的东西:

 + top : 123
   | + Node 1 : 29
   |   | + subnode 0 : 90
   |   |     + sdhasj : 232
   |   | + subnode 1 : 38
   |   | + subnode 2 : 49
   |   | + subnode 8 : 39
   |     + subnode 9 : 47
     + Node 2 : 51
       | + subnode 0 : 89
       |     + sdhasj : 232
       | + subnode 1 : 33
         + subnode 3 : 57

诀窍是传递字符串作为缩进,并特别对待最后一个子项:

class Node
{    
   public void PrintPretty(string indent, bool last)
   {
       Console.Write(indent);
       if (last)
       {
           Console.Write("\\-");
           indent += "  ";
       }
       else
       {
           Console.Write("|-");
           indent += "| ";
       }
       Console.WriteLine(Name);

       for (int i = 0; i < Children.Count; i++)
           Children[i].PrintPretty(indent, i == Children.Count - 1);
   }
}
将以这种样式输出:

\-root
  \-child
    |-child
    \-child
      |-child
      |-child
      \-child
        |-child
        |-child
        | |-child
        | \-child
        |   |-child
        |   |-child
        |   |-child
        |   \-child
        |     \-child
        |       \-child
        \-child
          |-child
          |-child
          |-child
          | \-child
          \-child
            \-child
递归 您需要跟踪缩进字符串,当您深入到树中时,缩进字符串会被修改。为了避免添加额外的
|
字符,您还需要知道节点是否是该集中的最后一个子节点

public static void PrintTree(Node tree, String indent, Bool last)
{
    Console.Write(indent + "+- " + tree.Name);
    indent += last ? "   " : "|  ";

    for (int i = 0; i < tree.Children.Count; i++)
    {
        PrintTree(tree.Children[i], indent, i == tree.Children.Count - 1);
    }
}
它将输出如下所示的文本:

root.PrintPretty("", true);
PrintTree(node, "", true)
+- root
   +- branch-A
   |  +- sibling-X
   |  |  +- grandchild-A
   |  |  +- grandchild-B
   |  +- sibling-Y
   |  |  +- grandchild-C
   |  |  +- grandchild-D
   |  +- sibling-Z
   |     +- grandchild-E
   |     +- grandchild-F
   +- branch-B
      +- sibling-J
      +- sibling-K
无递归 如果您恰好有一个非常深的树,并且您的调用堆栈大小有限,您可以改为执行静态、非递归树遍历以输出相同的结果:

public static void PrintTree(Node tree)
{
    List<Node> firstStack = new List<Node>();
    firstStack.Add(tree);

    List<List<Node>> childListStack = new List<List<Node>>();
    childListStack.Add(firstStack);

    while (childListStack.Count > 0)
    {
        List<Node> childStack = childListStack[childListStack.Count - 1];

        if (childStack.Count == 0)
        {
            childListStack.RemoveAt(childListStack.Count - 1);
        }
        else
        {
            tree = childStack[0];
            childStack.RemoveAt(0);

            string indent = "";
            for (int i = 0; i < childListStack.Count - 1; i++)
            {
                indent += (childListStack[i].Count > 0) ? "|  " : "   ";
            }

            Console.WriteLine(indent + "+- " + tree.Name);

            if (tree.Children.Count > 0)
            {
                childListStack.Add(new List<Node>(tree.Children));
            }
        }
    }
}
公共静态void打印树(节点树)
{
List firstStack=新列表();
添加(树);
List childListStack=新列表();
添加(第一个堆栈);
while(childListStack.Count>0)
{
List childStack=childListStack[childListStack.Count-1];
if(childStack.Count==0)
{
RemoveAt(childListStack.Count-1);
}
其他的
{
tree=childStack[0];
childStack.RemoveAt(0);
字符串缩进=”;
for(int i=0;i0)?“|”:”;
}
Console.WriteLine(缩进+“+-”+树名);
如果(tree.Children.Count>0)
{
添加(新列表(tree.Children));
}
}
}
}

我正在使用以下方法打印BST

private void print(Node root, String prefix) {
    if (root == null) {
    System.out.println(prefix + "+- <null>");
    return;
    }

    System.out.println(prefix + "+- " + root);
    print(root.left, prefix + "|  ");
    print(root.right, prefix + "|  ");
}
private void打印(节点根,字符串前缀){
if(root==null){
System.out.println(前缀+“+-”);
返回;
}
System.out.println(前缀+“+-”+根);
打印(root.left,前缀+“|”);
打印(root.right,前缀+“|”);
}
以下是输出

+- 43(l:0, d:1)
|  +- 32(l:1, d:3)
|  |  +- 10(l:2, d:0)
|  |  |  +- <null>
|  |  |  +- <null>
|  |  +- 40(l:2, d:2)
|  |  |  +- <null>
|  |  |  +- 41(l:3, d:0)
|  |  |  |  +- <null>
|  |  |  |  +- <null>
|  +- 75(l:1, d:5)
|  |  +- 60(l:2, d:1)
|  |  |  +- <null>
|  |  |  +- 73(l:3, d:0)
|  |  |  |  +- <null>
|  |  |  |  +- <null>
|  |  +- 100(l:2, d:4)
|  |  |  +- 80(l:3, d:3)
|  |  |  |  +- 79(l:4, d:2)
|  |  |  |  |  +- 78(l:5, d:1)
|  |  |  |  |  |  +- 76(l:6, d:0)
|  |  |  |  |  |  |  +- <null>
|  |  |  |  |  |  |  +- <null>
|  |  |  |  |  |  +- <null>
|  |  |  |  |  +- <null>
|  |  |  |  +- <null>
|  |  |  +- <null>
+-43(l:0,d:1)
|+-32(l:1,d:3)
||+-10(l:2,d:0)
|  |  |  +- 
|  |  |  +- 
||+-40(l:2,d:2)
|  |  |  +- 
|| |+-41(l:3,d:0)
|  |  |  |  +- 
|  |  |  |  +- 
|+-75(长:1,长:5)
||+-60(l:2,d:1)
|  |  |  +- 
|| |+-73(l:3,d:0)
|  |  |  |  +- 
|  |  |  |  +- 
||+-100(l:2,d:4)
|| |+-80(l:3,d:3)
|| | |+-79(l:4,d:2)
|| | | |+-78(l:5,d:1)
|| | | | |+-76(l:6,d:0)
|  |  |  |  |  |  |  +- 
|  |  |  |  |  |  |  +- 
|  |  |  |  |  |  +- 
|  |  |  |  |  +- 
|  |  |  |  +- 
|  |  |  +- 

以下是@Will(目前接受的)答案的变体。这些变化是:

  • 这将使用Unicode符号而不是ASCII,以获得更美观的外观
  • 根元素没有缩进
  • 组的最后一个子级在其后面添加了一个“空白”行(便于直观分析)
  • 作为伪代码呈现,以便在C++之外更容易使用:

    def打印层次结构(项目,缩进)
    kids=findChildren(项目)#获得一个可编辑的集合
    标签=标签(项目)#项目的印刷版本
    last=isLastSibling(项)#这是其父项的最后一个子项吗?
    root=isRoot(item)#这是树中的第一项吗?
    如果根那么
    打印(标签)
    其他的
    #Unicode字符U+2514或U+251C后跟U+2574
    打印(缩进+(最后?)└╴' : '├╴') + 标签)
    如果最后我是空的(孩子们),那么
    #在最后一个子项后添加一个空行
    打印(缩进)
    结束
    #空格或U+2502后跟空格
    缩进=缩进+(最后?'':'│ ')
    结束
    每一个孩子都在做什么
    打印层次结构(子级,缩进)
    结束
    结束
    打印层次结构(根“”)
    
    样本结果:

    正文
    ├╴漆黑
    ├╴车漆
    ├╴黑漆材料
    ├╴漆蓝色
    ├╴标志
    │ └╴形象
    │
    ├╴铬
    ├╴塑料
    ├╴铝
    │ └╴形象
    │
    └╴法布里克达尔
    
    这是Joshua Stachowski答案的通用版本。Joshua Stachowski答案的优点在于它不需要实际的节点类来实现任何额外的方法,而且看起来也很不错

    我使他的解决方案通用,可以用于任何类型,而无需修改代码

        public static void PrintTree<T>(T rootNode,
                                        Func<T, string> nodeLabel, 
                                        Func<T, List<T>> childernOf)
                {
                    var firstStack = new List<T>();
                    firstStack.Add(rootNode);
    
                    var childListStack = new List<List<T>>();
                    childListStack.Add(firstStack);
    
                    while (childListStack.Count > 0)
                    {
                        List<T> childStack = childListStack[childListStack.Count - 1];
    
                        if (childStack.Count == 0)
                        {
                            childListStack.RemoveAt(childListStack.Count - 1);
                        }
                        else
                        {
                            rootNode = childStack[0];
                            childStack.RemoveAt(0);
    
                            string indent = "";
                            for (int i = 0; i < childListStack.Count - 1; i++)
                            {
                                indent += (childListStack[i].Count > 0) ? "|  " : "   ";
                            }
    
                            Console.WriteLine(indent + "+- " + nodeLabel(rootNode));
                            var children = childernOf(rootNode);
                            if (children.Count > 0)
                            {
                                childListStack.Add(new List<T>(children));
                            }
                        }
                    }
                }
    

    不使用递归而具有完全可选性的最佳方法是`

    publicstaticvoiddirectorytree(字符串完整路径)
    {
    string[]directories=fullPath.Split('\\');
    字符串子路径=”;
    int cursorUp=0;
    int cursorLeft=0;
    对于(int i=0;i!f.Attributes.hasvag(FileAttributes.Hidden)).Select(f=>f.Name.ToArray();
    var folders=directory.GetDirectories().Where(f=>!f.Attributes.hasvag(FileAttributes.Hidden)).Select(f=>f.Name.ToArray();
    int longestFolder=folders.Length!=0?(folders)。其中(s=>s.Length==folders.Max(m=>m.Length)).First()。长度:0;
    int longestFle=files.Length!=0?(files)。其中(s=>s.Length==files.Max(m=>m.Length)).First().Length:0;
    int longestName=3+(使用(y,x)坐标的longestName文件夹
    C代码如下:

    void printVLine(wchar_t token, unsigned short height, unsigned short y, unsigned short x);
    const static wchar_t TREE_VLINE = L'┃';
    const static wchar_t TREE_INBRANCH[] = L"┣╾⟶ ";
    const static wchar_t TREE_OUTBRANCH[] = L"┗╾⟶ ";
    
    typedef void (*Printer)(void * whateverYouWant);
    const static unsigned int  INBRANCH_SIZE = sizeof(TREE_INBRANCH) / sizeof(TREE_INBRANCH[0]);
    const static unsigned int OUTBRANCH_SIZE = sizeof(TREE_OUTBRANCH) / sizeof(TREE_OUTBRANCH[0]);
    
    size_t Tree_printFancy(Tree * self, int y, int x, Printer print){
        if (self == NULL) return 0L;
        //
        size_t descendants = y;
        move(y, x);
        print(Tree_at(self));
        if (!Tree_isLeaf(self)){ // in order not to experience unsigned value overflow in while()
            move(++y, x); 
            size_t i = 0;
            while(i < Tree_childrenSize(self) - 1){
                wprintf(TREE_INBRANCH);
                size_t curChildren = Tree_printFancy(
                       Tree_childAt(self, i), y, x + INBRANCH_SIZE, print
                );
                printVLine(TREE_VLINE, curChildren , y + 1, x);
                move((y += curChildren), x);
                ++i;
            }
            wprintf(TREE_OUTBRANCH); 
            y += Tree_printFancy(       // printing outermost child
                Tree_childAt(self, i), y, x + OUTBRANCH_SIZE, print
            ) - 1;
        }   
        return y - descendants + 1;
    }
    
    void printVLine(wchar_t标记、无符号短高度、无符号短y、无符号短x);
    常量静态wchar_t TREE_VLINE=L'┃';
    恒定静态wchar_t TREE_in Branch[]=L”┣╾⟶ ";
    常量静态wchar_t树_分支[]=L”┗╾⟶ ";
    typedef void(*打印机)(void*任何您想要的);
    常量静态无符号int-INBRANCH\u SIZE=sizeof(TREE\u-INBRANCH)
    
        public static void PrintTree<T>(T rootNode,
                                        Func<T, string> nodeLabel, 
                                        Func<T, List<T>> childernOf)
                {
                    var firstStack = new List<T>();
                    firstStack.Add(rootNode);
    
                    var childListStack = new List<List<T>>();
                    childListStack.Add(firstStack);
    
                    while (childListStack.Count > 0)
                    {
                        List<T> childStack = childListStack[childListStack.Count - 1];
    
                        if (childStack.Count == 0)
                        {
                            childListStack.RemoveAt(childListStack.Count - 1);
                        }
                        else
                        {
                            rootNode = childStack[0];
                            childStack.RemoveAt(0);
    
                            string indent = "";
                            for (int i = 0; i < childListStack.Count - 1; i++)
                            {
                                indent += (childListStack[i].Count > 0) ? "|  " : "   ";
                            }
    
                            Console.WriteLine(indent + "+- " + nodeLabel(rootNode));
                            var children = childernOf(rootNode);
                            if (children.Count > 0)
                            {
                                childListStack.Add(new List<T>(children));
                            }
                        }
                    }
                }
    
     PrintTree(rootNode, x => x.ToString(), x => x.Children);
    
    public static void DirectoryTree(string fullPath)
        {
        string[] directories = fullPath.Split('\\');
        string subPath = "";
        int cursorUp = 0;
        int cursorLeft = 0;
    
        for (int i = 0; i < directories.Length-1; i++)
        {
            subPath += directories[i] + @"\";
            DirectoryInfo directory = new DirectoryInfo(subPath);
            var files = directory.GetFiles().Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden)).Select(f => f.Name).ToArray();
            var folders = directory.GetDirectories().Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden)).Select(f => f.Name).ToArray();             
            int longestFolder = folders.Length != 0 ? (folders).Where(s => s.Length == folders.Max(m => m.Length)).First().Length:0;
            int longestFle = files.Length != 0? (files).Where(s => s.Length == files.Max(m => m.Length)).First().Length : 0;
            int longestName =3 + (longestFolder <= longestFle ? longestFle:longestFolder)<=25? (longestFolder <= longestFle ? longestFle : longestFolder) : 26;
            int j = 0;
    
            for (int k = 0; k < folders.Length; k++)
            {
                folders[k] = folders[k].Length <= 25 ? folders[k] : (folders[k].Substring(0, 22) + "...");
    
                if (folders[k] != directories[i + 1])
                {
                    Console.SetCursorPosition(cursorLeft, cursorUp + j);
                    Console.WriteLine("+" + folders[k]);
                    j++;
                }
                else
                {
                    if (i != directories.Length - 2)
                    {
                        Console.SetCursorPosition(cursorLeft, cursorUp + j);
                        Console.WriteLine("-" + folders[k] + new string('-', longestName - directories[i + 1].Length) + "--\u261B");
                        j++;
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.SetCursorPosition(cursorLeft, cursorUp + j);
                        Console.WriteLine("***"+ folders[k] + "***");
                        Console.ForegroundColor = ConsoleColor.Gray;
                        j++;
                    }
                }
            }
    
            for(int k = 0; k <  files.Length; k++)
            {
                files[k] = files[k].Length <= 25 ? files[k] : (files[k].Substring(0, 22) + "...");
                Console.SetCursorPosition(cursorLeft, cursorUp + j);
                Console.WriteLine("+" + files[k]);
                j++;
            }
    
            cursorUp += Array.IndexOf(folders, directories[i+1]) + 1;
            cursorLeft += longestName+3;
        }
    }
    
    void printVLine(wchar_t token, unsigned short height, unsigned short y, unsigned short x);
    const static wchar_t TREE_VLINE = L'┃';
    const static wchar_t TREE_INBRANCH[] = L"┣╾⟶ ";
    const static wchar_t TREE_OUTBRANCH[] = L"┗╾⟶ ";
    
    typedef void (*Printer)(void * whateverYouWant);
    const static unsigned int  INBRANCH_SIZE = sizeof(TREE_INBRANCH) / sizeof(TREE_INBRANCH[0]);
    const static unsigned int OUTBRANCH_SIZE = sizeof(TREE_OUTBRANCH) / sizeof(TREE_OUTBRANCH[0]);
    
    size_t Tree_printFancy(Tree * self, int y, int x, Printer print){
        if (self == NULL) return 0L;
        //
        size_t descendants = y;
        move(y, x);
        print(Tree_at(self));
        if (!Tree_isLeaf(self)){ // in order not to experience unsigned value overflow in while()
            move(++y, x); 
            size_t i = 0;
            while(i < Tree_childrenSize(self) - 1){
                wprintf(TREE_INBRANCH);
                size_t curChildren = Tree_printFancy(
                       Tree_childAt(self, i), y, x + INBRANCH_SIZE, print
                );
                printVLine(TREE_VLINE, curChildren , y + 1, x);
                move((y += curChildren), x);
                ++i;
            }
            wprintf(TREE_OUTBRANCH); 
            y += Tree_printFancy(       // printing outermost child
                Tree_childAt(self, i), y, x + OUTBRANCH_SIZE, print
            ) - 1;
        }   
        return y - descendants + 1;
    }