【问题标题】:Flat List<T> to structured List<T> via recursion in C#?通过 C# 中的递归将平面 List<T> 转换为结构化 List<T>?
【发布时间】:2015-02-08 08:50:31
【问题描述】:

我有一个现有的课程:

public class Product
{
 public int Id { get; set; }
 public int ParentId { get; set; }
 public string Info { get; set; }
}

我也有一个给定的List&lt;Product&gt;

var Lst = new List<Product>();
Lst.Add(new Product{ Id=1,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=2,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=3,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=60,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=61,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=62,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=72,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=90,ParentId=2,Info="a"});

可视化:

1
|
+---60
|
+---61
     |
     +---62
     |
     +---72

2
|
+---90

3

如您所见,List&lt;&gt;flat。 (所有项目在列表中处于同一级别。只是id,parentId代表层次结构)

现在 - 我需要创建结构 List&lt;&gt; 以便 List 中的每个项目将在 内部 其父对象:

所以我创建了一个额外的结构类来保存这个结构:

public class Node
{
 public Product Product { get; set; }
 public List<Node> LstNodes  { get; set; }
}

所以现在我可以做:

List<Node> lstNodes = new List<Node>();

最初我可以添加根目录:

lstNodes=Lst.Where(product=>product.ParentId==0).Select(node=>new Node{Product=node}).ToList();

现在我可以开始递归插入他们父母的项目。

那么问题出在哪里?

问题:

我想避免首先插入根元素(根是ParentId=0)。

有没有办法用 one 递归方法(包括根)来做到这一点?

期望的结果:lstNodes 中的 3 个节点,每个节点递归地都有其子节点。

【问题讨论】:

  • 如果您传递带有 Id->Node 的字典以将孩子与他们的父母联系起来,那么您可以以 declerative 风格执行此操作。但在这种情况下,我会以老式的foreach 循环样式执行此操作;) - 顺便说一句:应该为 Id 排序列表以使其变得非常容易,但它似乎已经是
  • 老实说,我无法弄清楚如何递归地执行此操作...

标签: c# generics recursion


【解决方案1】:

这样的?

List<Node> GetNodes(List<Product> lst, int parentId = 0)
{
    var childProducts = lst.Where(x=>x.ParentId == parentId);
    return childProducts
           .Select(x=> new Node { Product = x, LstNodes = GetNodes(lst, x.Id)}
           .ToList();
}

纯通用版本:

class Node<T>
{
    public T Item { get; set; }
    public List<Node<T>> LstNodes  { get; set; }
}

List<Node<T>> GetNodes<T>(List<T> lst, Func<T, int> idSelector, Func<T, int> parentIdSelector, int parentId = 0)
{
    var childProducts = lst.Where(x=>parentIdSelector(x) == parentId);
    return childProducts
           .Select(x=> new Node<T> { Item = x, LstNodes = GetNodes<T>(lst, idSelector,parentIdSelector, idSelector(x))})
           .ToList();
}

GetNodes(Lst,x=>x.Id,x=>x.ParentId, 0);

【讨论】:

  • @RoyiNamir 是的,它缺少通用参数。修复了我的代码。
  • @RoyiNamir 是的。对不起。并没有真正费心通过 VS 运行它。
  • @RoyiNamir 我按照您的要求使用递归。查看 3dGrabber 的答案以获得更好的解决方案。我不确定是否可以做出更好的递归解决方案。
  • 是的,我知道 :-) 只是在想自己是否可能仍然使用递归但要减少 on(2)。
【解决方案2】:

你可以试试这个:

    static List<Node> BuildTree(List<Product> lst, int topID) 
    {
        var tree = Enumerable.Empty<Node>().ToList();
        if (lst == null)
        {
            return tree;
        }

        foreach (var product in lst)
        {
            if (product.ParentId == topID)
            {
                Node node = new Node 
                {
                    Product = product,
                    LstNodes = BuildTree(lst.Where(p => p.ParentId == product.Id).ToList(), product.Id)
                };

                tree.Add(node);
            }
        }

        return tree;
    }

您可以拨打:List&lt;Node&gt; roots = BuildTree(Lst, 0);

【讨论】:

    【解决方案3】:

    目前提出的解决方案有效,但每个节点迭代整个集合一次。
    这使得它们 O(n2) 不适合大型集合(数据库,...)。
    这是一个在线性时间内运行的解决方案:

    var products = new List<Product>();
    products.Add(...);
    ...
    
    var nodes          = products.Select(p => new Node { Product = p }).ToList(); // 1
    var nodeWithId     = nodes.ToDictionary(n => n.Product.Id);                   // 2
    var parentChildren = nodes.Where(n => n.Product.ParentId != 0)
                              .ToLookup(n => nodeWithId[n.Product.ParentId]);     // 3
    
    foreach (var pc in parentChildren)  // 4
    {
        var parent   = pc.Key;
        var children = pc.ToList();
    
        parent.LstNodes = children;
    }
    

    解释:

    1. 从产品创建节点。
    2. 创建一个字典,让我们可以轻松找到具有给定 id 的节点
    3. 创建一个查找(每个键具有多个值的字典),为我们提供给定节点的子节点。
    4. 遍历查找,将孩子与其父母联系起来

    【讨论】:

    • 已修复。忘了根节点的特殊情况:没有NodeId 0。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-26
    • 2019-05-20
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    相关资源
    最近更新 更多