簡體   English   中英

如何加快此遞歸功能?

[英]How can I speed up this recursive function?

我有一個遞歸函數,該函數從IEnumerable約2000條記錄中構建節點列表。 該過程目前大約需要9秒鍾才能完成,已成為主要的性能問題。 該功能用於:

a)對節點進行分層排序

b)計算每個節點的深度

這是一個簡化的示例:

public class Node
{
    public string Id { get; set; }
    public string ParentId { get; set; }
    public int Depth { get; set; }
}

private void GetSortedList()
{
// next line pulls the nodes from the DB, not included here to simplify the example         
IEnumerable<Node> ie = GetNodes();

    var l = new List<Node>();
    foreach (Node n in ie)
    {
        if (string.IsNullOrWhiteSpace(n.ParentId))
        {
            n.Depth = 1;
            l.Add(n);
            AddChildNodes(n, l, ie);
        }
    }
}

private void AddChildNodes(Node parent, List<Node> newNodeList, IEnumerable<Node> ie)
{
    foreach (Node n in ie)
    {
        if (!string.IsNullOrWhiteSpace(n.ParentId) && n.ParentId == parent.Id)
        {
            n.Depth = parent.Depth + 1;
            newNodeList.Add(n);
            AddChildNodes(n, newNodeList, ie);
        }
    }
}

重寫此代碼以最大化性能的最佳方法是什么? 我已經嘗試過yield關鍵字,但是不確定是否可以得到想要的結果。 我也讀過有關使用堆棧的信息,但是我發現沒有一個示例使用父ID(而是使用子節點列表),因此我對如何使用它感到有些困惑。

遞歸不是導致您的性能問題的原因。 真正的問題是,在對AddChildNodes每個遞歸調用上,您遍歷整個列表以查找當前父級的子級,因此您的算法最終為O(n ^ 2)。

為了解決這個問題,您可以創建一個字典,該字典針對每個節點ID給出其所有子項的列表。 可以通過列表的一次操作來完成。 然后,您可以從根ID(“”)開始並遞歸訪問其每個子級(即“深度優先遍歷”)。 這將訪問每個節點一次。 因此整個算法為O(n)。 代碼如下所示。

打完電話后GetSortedList ,排序結果是result 請注意,您可以讓childrenresult的局部變量GetSortedList ,並將它們作為參數傳遞給DepthFirstTraversal ,如果你喜歡。 但這不必要地減慢了遞歸調用的速度,因為這兩個參數在每個遞歸調用上始終具有相同的值。

您可以使用堆棧擺脫遞歸,但是性能提升可能不值得。

Dictionary<string, List<Node>> children = null; 
List<Node> result = null;

private void GetSortedList()
{
    var ie = data;
    children = new Dictionary<string,List<Node>>();

    // construct the dictionary 
    foreach (var n in ie) 
    {
        if (!children.ContainsKey(n.ParentId)) 
        {
            children[n.ParentId] =  new List<Node>();
        }
        children[n.ParentId].Add(n);
    }

    // Depth first traversal
    result = new List<Node>();
    DepthFirstTraversal("", 1);

    if (result.Count() !=  ie.Count()) 
    {
        // If there are cycles, some nodes cannot be reached from the root,
        // and therefore will not be contained in the result. 
        throw new Exception("Original list of nodes contains cycles");
    }
}

private void DepthFirstTraversal(string parentId, int depth)
{
    if (children.ContainsKey(parentId))
    {
        foreach (var child in children[parentId])
        {
            child.Depth = depth;
            result.Add(child);
            DepthFirstTraversal(child.Id, depth + 1);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM