web-dev-qa-db-fra.com

Construire une liste de types d'arbres en vérifiant récursivement la relation parent-enfant C #

J'ai une classe qui a une liste d'elle-même afin qu'elle puisse être représentée dans une structure arborescente.

Je tire une liste plate de ces classes et je veux la aplatir.

public class Group
{
     public int ID {get;set;}

     public int? ParentID {get;set;}

     public List<Group> Children {get;set;}

}

Je veux pouvoir faire ce qui suit

List<Group> flatList = GetFlatList() //I CAN ALREADY DO THIS
List<Group> tree = BuildTree(flatList);

Le ParentID lié à la propriété ID sur son groupe parent si cela n'était pas évident.

MODIFIER

Il y a une certaine confusion quant à la raison pour laquelle je renvoie une liste et non un seul objet.

Je construis un élément d'interface utilisateur qui a une liste d'éléments, chacun ayant pourquoi un enfant. Ainsi, la liste initiale N'A PAS de nœud racine. Il semble que toutes les solutions jusqu'à présent ne fonctionnent pas.

Cela signifie que j'ai essentiellement besoin d'une liste de structures de type arbre utilisant la classe Group.

20
TheJediCowboy

Je ne sais pas pourquoi vous voulez que votre méthode BuildTree retourne List<Group> - l'arborescence doit avoir un nœud racine, vous devez donc vous attendre à ce qu'il retourne un seul élément Group, pas une liste.

Je créerais une méthode d'extension sur IEnumerable<Group>:

public static class GroupEnumerable
{
    public static IList<Group> BuildTree(this IEnumerable<Group> source)
    {
        var groups = source.GroupBy(i => i.ParentID);

        var roots = groups.FirstOrDefault(g => g.Key.HasValue == false).ToList();

        if (roots.Count > 0)
        {
            var dict = groups.Where(g => g.Key.HasValue).ToDictionary(g => g.Key.Value, g => g.ToList());
            for (int i = 0; i < roots.Count; i++)
                AddChildren(roots[i], dict);
        }

        return roots;
    }

    private static void AddChildren(Group node, IDictionary<int, List<Group>> source)
    {
        if (source.ContainsKey(node.ID))
        {
            node.Children = source[node.ID];
            for (int i = 0; i < node.Children.Count; i++)
                AddChildren(node.Children[i], source);
        }
        else
        {
            node.Children = new List<Group>();
        }
    }
}

tilisation

var flatList = new List<Group>() {
    new Group() { ID = 1, ParentID = null },    // root node
    new Group() { ID = 2, ParentID = 1 },
    new Group() { ID = 3, ParentID = 1 },
    new Group() { ID = 4, ParentID = 3 },
    new Group() { ID = 5, ParentID = 4 },
    new Group() { ID = 6, ParentID = 4 }
};


var tree = flatList.BuildTree();
38
MarcinJuraszek

J'ai essayé les solutions suggérées et j'ai compris qu'elles nous renseignaient sur la complexité de O (n ^ 2).

Dans mon cas (j'ai environ 50 000 éléments à intégrer dans l'arborescence), c'était complètement inacceptable.

Je suis arrivé à la solution suivante (en supposant que chaque élément n'a qu'un seul parent et que tous les parents existent dans la liste) avec la complexité O (n * log (n)) [n fois getById, getById a O(log(n)) complexité]:

static List<Item> BuildTreeAndReturnRootNodes(List<Item> flatItems)
{
    var byIdLookup = flatItems.ToLookup(i => i.Id);
    foreach (var item in flatItems)
    {
        if (item.ParentId != null)
        {
            var parent = byIdLookup[item.ParentId.Value].First();
            parent.Children.Add(item);
        }
    }
    return flatItems.Where(i => i.ParentId == null).ToList();
}

Extrait de code complet:

class Program
{
    static void Main(string[] args)
    {
        var flatItems = new List<Item>()
        {
            new Item(1),
            new Item(2),
            new Item(3, 1),
            new Item(4, 2),
            new Item(5, 4),
            new Item(6, 3),
            new Item(7, 5),
            new Item(8, 2),
            new Item(9, 3),
            new Item(10, 9),
        };
        var treeNodes = BuildTreeAndReturnRootNodes(flatItems);
        foreach (var n in treeNodes)
        {
            Console.WriteLine(n.Id + " number of children: " + n.Children.Count);
        }
    }
    // Here is the method
    static List<Item> BuildTreeAndReturnRootNodes(List<Item> flatItems)
    {
        var byIdLookup = flatItems.ToLookup(i => i.Id);
        foreach (var item in flatItems)
        {
            if (item.ParentId != null)
            {
                var parent = byIdLookup[item.ParentId.Value].First();
                parent.Children.Add(item);
            }
        }
        return flatItems.Where(i => i.ParentId == null).ToList();
    }
    class Item
    {
        public readonly int Id;
        public readonly int? ParentId;

        public Item(int id, int? parent = null)
        {
            Id = id;
            ParentId = parent;
        }
        public readonly List<Item> Children = new List<Item>();
    }
}
8
Dmitry Andrievsky