【问题标题】:Replacing recursive with loop用循环替换递归
【发布时间】:2013-02-01 08:29:11
【问题描述】:

我正在处理一个 ASP.Net 页面,其中有树视图。在树视图中,一些节点具有嵌套节点,如分支。我的自定义对象列表中有以下格式的数据:

Id、描述、parentId

现在,我正在使用一个函数来递归地将节点添加到树视图中。以下为sn-p代码:

private bool findParentAddNode(string id, string description, string parentid, ref List<CustomTreeNode> treeList)
{
    bool isFound = false;
    foreach (CustomTreeNode node in treeList)
    {
        if (node.id == parentid)//if current node is parent node, add in it as its child
        {
            node.addChild(id, description, parentid);
            isFound = true;
            break;
        }
        else if (node.listOfChildNodes != null)//have child nodes
        {
            isFound = findParentAddNode(id, description, parentid, ref node.listOfChildNodes);
            if (isFound)
                break;
        }

    }

    return isFound;
}

上述技术效果很好,但是对于超过 30K 的节点,它的性能很慢。请提出一个算法来用循环替换这个递归调用。

【问题讨论】:

  • 你确定递归是问题吗?除非你的树形很奇怪,否则不应该有那么多级别。
  • 我相信 for loops 在 30K 节点下仍然会很慢。如果你不使用相同的参数调用函数,递归方法很好
  • @NadeemJamali Visual Studio 应该包含一个分析器。此外,您可能想查看介绍性 AI / 机器学习教科书,这些教科书很好地涵盖了树搜索算法。 (他们会告诉你,对于递归树搜索,如果你不知道结果应该在树中有多深,那么迭代加深的 DFS 往往比 BFS 具有更好的属性。)
  • 看到这篇文章http://stackoverflow.com/q/308816/1174942。我以前用过JetBrains dotTrace。它很棒,但不是免费的。
  • 看起来您在整个现有树中扫描您插入的每个节点。这将导致一个 n^2 算法。我建议分两次添加节点:首先创建所有具有 id 和描述的节点并将它们放在地图(哈希表)中。然后再次遍历所有节点,在映射中查找正确的父节点并将该节点附加为子节点。

标签: c# algorithm loops recursion


【解决方案1】:

当它沿着树递归时,代码对子节点列表进行线性搜索。

这意味着对于随机分布的父 ID,在将 N 个节点添加到树后,它会在添加第 N+1 个节点之前平均搜索 N/2 个父节点。因此,节点数量的成本将为 O(N²)。

不是线性扫描,而是创建一个 id 到节点的索引并使用它来快速找到父节点。当您创建节点并将其添加到树中时,还要将其添加到 Dictionary&lt;int,CustomTreeNode&gt;。当你想将一个节点添加到父节点时,在索引中找到父节点并添加它。如果addChild返回它创建的孩子,那么代码变成:

Dictionary<int,CustomTreeNode> index = new Dictionary<int,CustomTreeNode>();

private bool findParentAddNode(string id, string description, string parentid)
{
    if ( !nodeIndex.TryGetValue ( parentid, out parentNode ) ) 
        return false;

    index[id] = parentNode.addChild(id, description, parentid);

    return true;
}

在使用findParentAddNode 之前,您需要将树的根添加到索引中。

【讨论】:

    【解决方案2】:

    您可以使用 for xml 从 sql server 数据库返回 xml 格式的数据 然后将其绑定到树视图控件。

    【讨论】:

    • -1:这不是 OP 真正在做的事情,他正在修改一些他已经拥有的树数据。
    • 绑定数据不是问题,但实际问题是将节点(我有记录/对象格式的节点)添加到树视图及其正确位置的分支。
    【解决方案3】:

    广度优先搜索的迭代版本应该类似于以下内容:

    var rootNodes = new List<CustomTreeNode> { new CustomTreeNode() };
    
    while (rootNodes.Count > 0) {
        var nextRoots = new List<CustomTreeNode>();
        foreach (var node in rootNodes) {
            // process node here
            nextRoots.AddRange(node.CustomTreeNode);
        }
        rootNodes = nextRoots;
    }
    

    也就是说,这未经测试,并且由于它是 BFS,因此不是最佳的 w/r/t 内存。 (与 DFS 或迭代深化的 DFS 相比,内存使用是 O(n),而不是 O(log n)。)

    【讨论】:

    • 它看起来更好,我会测试它。谢谢毫驼。
    • @NadeemJamali 您可能还会考虑使用迭代 DFS 而不是 BFS,但这在 C# 中有点难看,因为您需要修改列表而不是使用 foreach。 (并且可能需要也可能不需要使用LinkedList,因为基于数组的List 因为频繁的前置。)进一步的微优化将初始化nextRoots,其容量是rootNodes.Count 的一个因素,基于估计你的树的分支因子来阻止调整大小。
    猜你喜欢
    • 2021-05-03
    • 2021-04-02
    • 2012-11-12
    • 1970-01-01
    • 2014-08-05
    • 1970-01-01
    • 2014-10-20
    • 1970-01-01
    • 2020-08-10
    相关资源
    最近更新 更多