How do I print out a tree structure?

前端 未结 8 1547
予麋鹿
予麋鹿 2020-11-27 12:42

I\'m trying to improve performance in our app. I\'ve got performance information in the form of a tree of calls, with the following node class:

public class         


        
相关标签:
8条回答
  • 2020-11-27 13:20

    Using (y, x) coordinates

    C code here:

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

    It is applicable rather for console printing. Function move(y, x) moves cursor to (y, x) location on the screen. The best part is, you may change style of output by changing variables TREE_VLINE, TREE_INBRANCH, TREE_OUTBRANCH, length of two last strings doesn't matter. And you can print whatever you like, by passing Printer function pointer, which will print the value of the current tree node. Output looks like this

    0 讨论(0)
  • 2020-11-27 13:21

    This is a generic version of Joshua Stachowski's answer. The good thing about Joshua Stachowski's answer is that it doesn't require the actual node class to implement any extra method and it looks nice as well.

    I made his solution generic which can be used for any type without modifying the code.

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

    Usage

     PrintTree(rootNode, x => x.ToString(), x => x.Children);
    
    0 讨论(0)
  • 2020-11-27 13:23

    With Recursion

    You'll need to keep track of an indentation string that's modified as you go deeper into the tree. To avoid adding extra | characters, you'll also need to know whether the Node is the last child in that set.

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

    When called like this:

    PrintTree(node, "", true)
    

    It will output text like this:

    +- 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
    

    Without Recursion

    If you happen to have a very deep tree and your call stack size is limited, you can instead do a static, non-recursive tree traversal to output the same result:

    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));
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 13:29

    The trick is to pass a string as the indent and to treat the last child specially:

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

    If called like this:

    root.PrintPretty("", true);
    

    will output in this style:

    \-root
      \-child
        |-child
        \-child
          |-child
          |-child
          \-child
            |-child
            |-child
            | |-child
            | \-child
            |   |-child
            |   |-child
            |   |-child
            |   \-child
            |     \-child
            |       \-child
            \-child
              |-child
              |-child
              |-child
              | \-child
              \-child
                \-child
    
    0 讨论(0)
  • 2020-11-27 13:29

    Create PrintNode method and use recursion:

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

    ANd then to print the whole tree just execute:

    topNode.PrintNode("");
    

    In my example it would give us something like that:

     + 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
    
    0 讨论(0)
  • 2020-11-27 13:29

    The best way with full optionality without using recursion is` https://github.com/tigranv/Useful_Examples/tree/master/Directory%20Tree

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题