270 votes

Structure de données arborescente en C#

Je cherchais une structure de données d'arbre ou de graphe en C#, mais je suppose qu'il n'y en a pas fournie. Un Examen Approfondi des Structures de Données Utilisant C# 2.0 en parle un peu. Y a-t-il une bibliothèque pratique couramment utilisée pour fournir cette fonctionnalité? Peut-être à travers un pattern stratégique pour résoudre les problèmes présentés dans l'article.

Je me sens un peu bête en implémentant mon propre arbre, tout comme je le ferais en implémentant ma propre ArrayList.

Je veux juste un arbre générique qui peut être déséquilibré. Pensez à un arbre de répertoires. C5 a l'air sympa, mais leurs structures d'arbre semblent être implémentées comme des arbres rouges-noirs équilibrés mieux adaptés à la recherche qu'à la représentation d'une hiérarchie de nœuds.

2 votes

Un peu plus d'arbres extrêmes : stackoverflow.com/questions/196294/… ;-)

0 votes

Y a-t-il une raison pour laquelle on ne peut pas inclure un TreeView dans le projet et l'utiliser ? Il n'est pas nécessaire de le montrer réellement à un utilisateur. Bien sûr, il existe plusieurs formes de projets où cela n'est pas une option. On peut toujours créer de nouvelles classes qui héritent par exemple de TreeNode s'il est nécessaire d'avoir une complexité spéciale ?

11 votes

Je considérerais que ce serait une mauvaise idée d'importer une bibliothèque UI entière pour un arbre très simple.

197voto

stimms Points 14986

Je déteste l'admettre mais j'ai fini par écrire ma propre classe arborescente en utilisant une liste chaînée. Sur une note sans rapport, je viens de découvrir cette chose ronde qui, lorsqu'elle est attachée à une chose que j'appelle un 'essieu', permet un transport plus facile des marchandises.

170voto

David Boike Points 7528

Mon meilleur conseil serait qu'il n'y a pas de structure de données d'arbre standard car il existe tellement de façons de l'implémenter qu'il serait impossible de couvrir toutes les bases avec une seule solution. Plus une solution est spécifique, moins elle est susceptible de s'appliquer à un problème donné. Je suis même agacé par les LinkedList - que se passe-t-il si je veux une liste chaînée circulaire ?

La structure de base que vous devrez mettre en œuvre sera une collection de nœuds, et voici quelques options pour vous aider à démarrer. Supposons que la classe Node est la classe de base de l'ensemble de la solution.

Si vous devez seulement naviguer dans l'arbre, alors une classe Node a besoin d'une liste d'enfants.

Si vous devez naviguer dans l'arbre, alors la classe Node a besoin d'un lien vers son nœud parent.

Construisez une méthode AddChild qui prend en charge tous les détails de ces deux points et toute autre logique métier qui doit être implémentée (limites d'enfants, tri des enfants, etc.)

6 votes

Personnellement, je ne verrais aucun inconvénient à ce qu'un arbre binaire auto-équilibré soit ajouté à la bibliothèque car cela demande un peu plus de travail que simplement utiliser une liste d'adjacence.

9 votes

@jk Je crois que SortedDictionary et SortedSet sont construits au-dessus des arbres rouge/noir, donc les utiliser devrait fonctionner.

0 votes

Jetez un œil au motif composite ;-) Exactement ce que vous cherchez

130voto

Aaron Gage Points 1374
delegate void TreeVisitor(T nodeData);

class NTree
{
    private T data;
    private LinkedList> children;

    public NTree(T data)
    {
         this.data = data;
        children = new LinkedList>();
    }

    public void AddChild(T data)
    {
        children.AddFirst(new NTree(data));
    }

    public NTree GetChild(int i)
    {
        foreach (NTree n in children)
            if (--i == 0)
                return n;
        return null;
    }

    public void Traverse(NTree node, TreeVisitor visitor)
    {
        visitor(node.data);
        foreach (NTree kid in node.children)
            Traverse(kid, visitor);
    }
}

Implémentation récursive simple... < 40 lignes de code... Vous devez simplement conserver une référence à la racine de l'arbre à l'extérieur de la classe, ou l'encapsuler dans une autre classe, peut-être la renommer en TreeNode ??

0 votes

Y a-t-il une raison pour la liste chaînée? J'ai tendance à utiliser List ou ObservableCollection. Que gagnez-vous en utilisant la liste chaînée. En passant, en utilisant cette méthode, il est vraiment facile de construire la méthode Descendants() sur l'arbre que LINQ to XML possède. Cela est très utile pour organiser les données en fonction d'un prédicat.

25 votes

Dans ce cas, en C# de toute façon, vous pourriez éviter d'écrire votre propre délégué et utiliser le délégué pré-fait Action : public void traverse(NTree node, Action visitor). La signature de Action est: void Action( T obj ). Il existe aussi des versions de 0 à 4 paramètres différents. Il existe également un délégué analogue pour les fonctions appelé Func<>.

1 votes

L'avantage de LinkedList est qu'il est plus efficace pour les tâches que nous lui attribuons ici, et il ne consomme que la quantité de mémoire nécessaire pour stocker autant de nœuds enfants. La seule action qui serait plus efficace avec une implémentation de List basée sur un tableau est la getChild(int), mais je m'attendrais à ce qu'elle soit invoquée rarement, généralement ajout et traversée seront utilisés, pour lesquels LinkedList est idéalement adapté. Compléter la mise en œuvre et ajouter la suppression peut compliquer les choses. Ce serait bien si les génériques de C# permettaient à l'utilisateur de spécifier l'implémentation de List la mieux adaptée à l'usage, mais ce n'est pas le cas.

67voto

Ronnie Overby Points 11402

Voici le mien, qui est très similaire à celui d'Aaron Gage, juste un peu plus conventionnel, à mon avis. Pour mes besoins, je n'ai rencontré aucun problème de performance avec List. Il serait assez facile de passer à un LinkedList si nécessaire.


namespace Overby.Collections
{
    public class TreeNode
    {
        private readonly T _value;
        private readonly List> _children = new List>();

        public TreeNode(T value)
        {
            _value = value;
        }

        public TreeNode this[int i]
        {
            get { return _children[i]; }
        }

        public TreeNode Parent { get; private set; }

        public T Value { get { return _value; } }

        public ReadOnlyCollection> Children
        {
            get { return _children.AsReadOnly(); }
        }

        public TreeNode AddChild(T value)
        {
            var node = new TreeNode(value) {Parent = this};
            _children.Add(node);
            return node;
        }

        public TreeNode[] AddChildren(params T[] values)
        {
            return values.Select(AddChild).ToArray();
        }

        public bool RemoveChild(TreeNode node)
        {
            return _children.Remove(node);
        }

        public void Traverse(Action action)
        {
            action(Value);
            foreach (var child in _children)
                child.Traverse(action);
        }

        public IEnumerable Flatten()
        {
            return new[] {Value}.Concat(_children.SelectMany(x => x.Flatten()));
        }
    }
}

0 votes

Pourquoi la propriété Value est-elle exposée lorsque vous la définissez dans le constructeur ? Cela la rend sujette à la manipulation APRÈS l'avoir déjà définie via le constructeur, n'est-ce pas ? Ne devrait-elle pas être définie en privé ?

0 votes

Bien sûr, pourquoi ne pas le rendre immutable? Édité.

1 votes

Merci! J'ai bien aimé ne pas avoir à écrire le mien. (Je ne peux toujours pas croire que ce n'est pas une chose qui existe nativement. J'ai toujours pensé que .net, ou du moins .net 4.0, avait tout.)

23voto

McKenzieG1 Points 5294

La généralement excellente C5 Generic Collection Library dispose de plusieurs structures de données basées sur des arbres différentes, y compris des ensembles, des sacs et des dictionnaires. Le code source est disponible si vous souhaitez étudier les détails de leur implémentation. (J'ai utilisé les collections C5 dans du code de production avec de bons résultats, bien que je n'ai pas spécifiquement utilisé l'une des structures d'arbre.)

7 votes

Je ne sais pas si les choses ont peut-être changé, mais pour l'instant, le livre est librement disponible en téléchargement au format PDF sur le site C5.

4 votes

Le manque de documentation n'est plus une préoccupation car il y a un fichier pdf de 272 pages qui complète la bibliothèque... Je ne peux pas commenter la qualité du code, mais en jugant par la qualité de la documentation, j'ai vraiment hâte de me plonger dedans ce soir !

2 votes

Selon ce que je comprends, cette bibliothèque C5 n'a pas du tout d'arbres, mais seulement certaines structures de données dérivées d'arbres.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X