J'essaie d'améliorer les performances de notre application. J'ai des informations sur les performances sous la forme d'une arborescence d'appels, avec la classe de nœud suivante:
public class Node
{
public string Name; // method name
public decimal Time; // time spent in method
public List<Node> Children;
}
Je veux imprimer l’arbre de manière à ce que je puisse voir des lignes entre les nœuds - quelque chose comme dans cette question . Qu'est-ce qu'un algorithme que je peux utiliser en C # pour le faire?
Edit: Évidemment, je dois utiliser la récursivité, mais mes tentatives n'arrêtent pas de placer les lignes au mauvais endroit. Ce que je demande, c’est un algorithme spécifique qui permettra d’imprimer l’arbre de manière agréable - les détails indiquant quand imprimer une ligne verticale et quand imprimer une ligne horizontale.
Edit: Il ne suffit pas d’utiliser des copies d’une chaîne pour indenter les nœuds. Je ne cherche pas
A
|-B
|-|-C
|-|-D
|-|-|-E
|-F
|-|-G
il doit être
A
+-B
| +-C
| +-D
| +-E
+-F
+-G
ou quelque chose de similaire, tant que l'arborescence est visible. Notez que C et D sont mis en retrait différemment de G - Je ne peux pas simplement utiliser une chaîne répétée pour mettre en retrait les nœuds.
L'astuce consiste à passer une chaîne en tant qu'indent et à traiter spécialement le dernier enfant:
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);
}
}
Si appelé comme ça:
root.PrintPretty("", true);
affichera dans ce 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
Vous devrez suivre une chaîne d'indentation modifiée au fur et à mesure que vous avancez dans l'arbre. Pour éviter d'ajouter des caractères |
supplémentaires, vous devez également savoir si le nœud est le dernier enfant de cet ensemble.
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);
}
}
Quand appelé comme ça:
PrintTree(node, "", true)
Il produira un texte comme ceci:
+- 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
Si vous avez une arborescence very deep et que la taille de la pile d'appels est limitée, vous pouvez effectuer une traversée d'arborescence statique et non récursive pour obtenir le même résultat:
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));
}
}
}
}
Créez la méthode PrintNode et utilisez la récursivité:
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 + " |");
}
}
Puis, pour imprimer l’arbre entier, exécutez simplement:
topNode.PrintNode("");
Dans mon exemple, cela nous donnerait quelque chose comme ça:
+ 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
Voici une variante de la réponse (actuellement acceptée) de @Will. Les changements sont les suivants:
Présenté sous forme de pseudo-code pour une consommation plus facile en dehors de C++:
def printHierarchy( item, indent )
kids = findChildren(item) # get an iterable collection
labl = label(item) # the printed version of the item
last = isLastSibling(item) # is this the last child of its parent?
root = isRoot(item) # is this the very first item in the tree?
if root then
print( labl )
else
# Unicode char U+2514 or U+251C followed by U+2574
print( indent + (last ? '└╴' : '├╴') + labl )
if last and isEmpty(kids) then
# add a blank line after the last child
print( indent )
end
# Space or U+2502 followed by space
indent = indent + (last ? ' ' : '│ ')
end
foreach child in kids do
printHierarchy( child, indent )
end
end
printHierarchy( root, "" )
Exemple de résultat:
Body
├╴PaintBlack
├╴CarPaint
├╴Black_Material
├╴PaintBlue
├╴Logo
│ └╴Image
│
├╴Chrome
├╴Plastic
├╴Aluminum
│ └╴Image
│
└╴FabricDark
j'utilise la méthode suivante pour imprimer un fichier 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 + "| ");
}
Voici la sortie.
+- 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>
Ceci est une version générique de la réponse de Joshua Stachowski. La bonne chose à propos de la réponse de Joshua Stachowski est qu’elle n’a pas besoin de la classe de nœuds elle-même pour implémenter une méthode supplémentaire et qu’elle a également l’air agréable.
J'ai fait sa solution générique qui peut être utilisée pour n'importe quel type sans modifier le 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);
Code C ici:
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;
}
Elle s’applique plutôt à l’impression sur console. La fonction move (y, x) déplace le curseur à l’emplacement (y, x) de l’écran . La meilleure partie est que vous pouvez modifier le style de sortie en modifiant les variables. .TREE_VLINE, TREE_INBRANCH, TREE_OUTBRANCH, la longueur des deux dernières chaînes n'a pas d'importance. Et vous pouvez imprimer ce que vous voulez en passant le pointeur de la fonction Imprimante, ce qui affichera la valeur du nœud d'arborescence actuel . La sortie ressemble à this