メモリノードツリーが非常に大きく、ツリーをトラバースする必要があります。各子ノードの戻り値を親ノードに渡します。これは、すべてのノードがルートノードまでデータをバブルするまで行う必要があります。
トラバーサルはこのように機能します。
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);
}
これは正常に機能しますが、コールスタックがノードツリーのサイズを制限するのではないかと心配しています。
Execute
への再帰呼び出しが行われないようにコードを書き換えるにはどうすればよいですか?
次に、再帰を使用しない汎用ツリートラバーサルの実装を示します。
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);
}
}
あなたの場合、それを次のように呼び出すことができます:
IEnumerable<Node> allNodes = Traverse(pRoot, node => node.Children);
深さを優先するのではなく、呼吸を優先して検索する場合は、Queue
ではなくStack
を使用してください。 PriorityQueue
を使用して、最適な最初の検索を行います。
事前にツリーの深さの見積もりがある場合、スタックサイズを調整するだけで十分でしょうか?バージョン2.0以降のC#では、新しいスレッドを開始するたびにこれが可能です。以下を参照してください。
そうすれば、より複雑なものを実装する必要なく、再帰的なコードを保持できます。もちろん、独自のスタックを使用して非再帰的なソリューションを作成すると、時間とメモリの効率が向上する可能性がありますが、コードは今ほど単純ではないはずです。