今天我要去執行遍歷任意深度圖並將其壓扁成一個單一的可枚舉的方法。相反,我首先做了一些搜索,發現這個:高效圖遍歷 - 消除遞歸
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> enumerable, Func<T, IEnumerable<T>> recursivePropertySelector)
{
foreach (T item in enumerable)
{
yield return item;
IEnumerable<T> seqRecurse = recursivePropertySelector(item);
if (seqRecurse == null) continue;
foreach (T itemRecurse in Traverse(seqRecurse, recursivePropertySelector))
{
yield return itemRecurse;
}
}
}
理論上,這看起來不錯,但在實踐中,我發現它比顯著用等效手寫代碼(如情況需要)更多的表現不佳通過圖表來做任何需要做的事情。我懷疑這是因爲在這種方法中,對於它返回的每一個項目,堆棧必須放鬆到某個任意深度。
我還懷疑,如果遞歸被消除,這種方法會更高效地運行。我也碰巧不擅長消除遞歸。
有誰知道如何重寫這個方法來消除遞歸?
感謝您的任何幫助。編輯: 非常感謝所有的詳細迴應。我試着對原始解決方案與Eric的解決方案進行基準比較,而不是使用枚舉器方法,而是遞歸遍歷一個lambda,奇怪的是,lambda遞歸比其他兩種方法中的任何一種都快得多。
class Node
{
public List<Node> ChildNodes { get; set; }
public Node()
{
ChildNodes = new List<Node>();
}
}
class Foo
{
public static void Main(String[] args)
{
var nodes = new List<Node>();
for(int i = 0; i < 100; i++)
{
var nodeA = new Node();
nodes.Add(nodeA);
for (int j = 0; j < 100; j++)
{
var nodeB = new Node();
nodeA.ChildNodes.Add(nodeB);
for (int k = 0; k < 100; k++)
{
var nodeC = new Node();
nodeB.ChildNodes.Add(nodeC);
for(int l = 0; l < 12; l++)
{
var nodeD = new Node();
nodeC.ChildNodes.Add(nodeD);
}
}
}
}
nodes.TraverseOld(node => node.ChildNodes).ToList();
nodes.TraverseNew(node => node.ChildNodes).ToList();
var watch = Stopwatch.StartNew();
nodes.TraverseOld(node => node.ChildNodes).ToList();
watch.Stop();
var recursiveTraversalTime = watch.ElapsedMilliseconds;
watch.Restart();
nodes.TraverseNew(node => node.ChildNodes).ToList();
watch.Stop();
var noRecursionTraversalTime = watch.ElapsedMilliseconds;
Action<Node> visitNode = null;
visitNode = node =>
{
foreach (var child in node.ChildNodes)
visitNode(child);
};
watch.Restart();
foreach(var node in nodes)
visitNode(node);
watch.Stop();
var lambdaRecursionTime = watch.ElapsedMilliseconds;
}
}
其中TraverseOld是最初的方法,TraverseNew是Eric的方法,顯然lambda是lambda。
在我的機器上,TraverseOld需要10127毫秒,TraverseNew需要3038毫秒,lambda遞歸需要1181毫秒。
這是典型的枚舉器方法(與收益率返回)可以採取3X只要立即執行相反?還是有其他事情在這裏?
乍一看,它看起來像這種方法被多次遞歸在每個層面上,即像天真的斐波那契f(x)= f(x - 1)+ f(x-2)'。 – mellamokb 2012-04-20 20:38:21
最後一個版本與前兩個版本的工作量差不多,特別是它不分配任何列表,將數組中的項目複製等。設想你是一個人口普查員,如果你決定只是挨家挨戶走,而不去打擾,你就知道寫下任何信息,那麼你的工作就會快得多。 – 2012-04-20 21:44:30
哪裏可以找到TraverseOld和TraverseNew 3年後? – user1756694 2016-02-19 10:06:03