J'ai une très grande arborescence de nœuds de mémoire et j'ai besoin de traverser l'arbre. Passer les valeurs renvoyées de chaque nœud enfant à leur nœud parent. Cela doit être fait jusqu'à ce que tous les nœuds aient leur bulle de données jusqu'au nœud racine.
La traversée fonctionne comme ça.
private Data Execute(Node pNode)
{
Data[] values = new Data[pNode.Children.Count];
for(int i=0; i < pNode.Children.Count; i++)
{
values[i] = Execute(pNode.Children[i]); // recursive
}
return pNode.Process(values);
}
public void Start(Node pRoot)
{
Data result = Execute(pRoot);
}
Cela fonctionne bien, mais je crains que la pile d'appels limite la taille de l'arborescence des nœuds.
Comment le code peut-il être réécrit afin qu'aucun appel récursif à Execute
ne soit effectué?
Voici une implémentation de traversée d'arbre à usage général qui n'utilise pas la récursivité:
public static IEnumerable<T> Traverse<T>(T item, Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>();
stack.Push(item);
while (stack.Any())
{
var next = stack.Pop();
yield return next;
foreach (var child in childSelector(next))
stack.Push(child);
}
}
Dans votre cas, vous pouvez l'appeler ainsi:
IEnumerable<Node> allNodes = Traverse(pRoot, node => node.Children);
Utilisez une Queue
au lieu d'une Stack
pour une recherche de respiration d'abord, plutôt que de profondeur d'abord. Utilisez un PriorityQueue
pour une meilleure première recherche.
Si vous avez au préalable une estimation de la profondeur de votre arbre, peut-être suffit-il que votre cas adapte la taille de la pile? En C # depuis la version 2.0, cela est possible chaque fois que vous démarrez un nouveau thread, voir ici:
De cette façon, vous pouvez conserver votre code récursif, sans avoir à implémenter quelque chose de plus complexe. Bien sûr, la création d'une solution non récursive avec votre propre pile peut être plus efficace en termes de temps et de mémoire, mais je suis sûr que le code ne sera pas aussi simple qu'il l'est pour l'instant.