Structure des données arborescentes en C#

Je cherchais une structure de données d'arbre ou de graphique en C # mais je suppose qu'il n'y en a pas fourni. Un examen approfondi des Structures de données utilisant C # 2.0 explique un peu pourquoi. Existe-t-il une bibliothèque pratique couramment utilisée pour fournir cette fonctionnalité? Peut-être à travers un modèle de stratégie pour résoudre les problèmes présentés dans l'article.

Je me sens un peu idiot d'implémenter mon propre arbre, tout comme j'implémenterais ma propre ArrayList.

Je veux juste un arbre générique qui peut être déséquilibré. Pensez à une arborescence de répertoires. C5 semble astucieux, mais leurs structures arborescentes semblent être implémentées comme des arbres rouges-noirs équilibrés mieux adaptés à la recherche que représentant une hiérarchie de nœuds.

220
demandé sur Tim B 2008-09-16 00:58:20

20 réponses

Mon meilleur conseil serait qu'il n'y a pas de structure de données arborescente standard car il y a tellement de façons de l'implémenter qu'il serait impossible de couvrir toutes les bases avec une solution. Plus une solution est spécifique, moins elle est susceptible d'être applicable à un problème donné. Je suis même ennuyé avec LinkedList-que faire si je veux une liste liée circulaire?

La structure de base que vous devrez implémenter sera une collection de nœuds, et voici quelques options pour vous aider à démarrer. Supposons que le nœud de classe est la classe de base de la solution entière.

Si vous devez uniquement naviguer dans l'arborescence, une classe Node a besoin d'une liste d'enfants.

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

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

136
répondu David Boike 2008-09-15 21:04:39

Je déteste l'admettre mais j'ai fini par écrire ma propre classe d'arbre en utilisant une liste lié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.

186
répondu stimms 2008-09-16 03:30:39
delegate void TreeVisitor<T>(T nodeData);

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

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

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

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

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

Implémentation récursive Simple...

106
répondu Aaron Gage 2015-03-30 13:36:27

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


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

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

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

        public TreeNode<T> Parent { get; private set; }

        public T Value { get { return _value; } }

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

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

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

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

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

        public IEnumerable<T> Flatten()
        {
            return new[] {Value}.Concat(_children.SelectMany(x => x.Flatten()));
        }
    }
}
46
répondu Ronnie Overby 2017-10-10 15:20:15

Encore une autre structure arborescente:

public class TreeNode<T> : IEnumerable<TreeNode<T>>
{

    public T Data { get; set; }
    public TreeNode<T> Parent { get; set; }
    public ICollection<TreeNode<T>> Children { get; set; }

    public TreeNode(T data)
    {
        this.Data = data;
        this.Children = new LinkedList<TreeNode<T>>();
    }

    public TreeNode<T> AddChild(T child)
    {
        TreeNode<T> childNode = new TreeNode<T>(child) { Parent = this };
        this.Children.Add(childNode);
        return childNode;
    }

    ... // for iterator details see below link
}

Exemple d'utilisation:

TreeNode<string> root = new TreeNode<string>("root");
{
    TreeNode<string> node0 = root.AddChild("node0");
    TreeNode<string> node1 = root.AddChild("node1");
    TreeNode<string> node2 = root.AddChild("node2");
    {
        TreeNode<string> node20 = node2.AddChild(null);
        TreeNode<string> node21 = node2.AddChild("node21");
        {
            TreeNode<string> node210 = node21.AddChild("node210");
            TreeNode<string> node211 = node21.AddChild("node211");
        }
    }
    TreeNode<string> node3 = root.AddChild("node3");
    {
        TreeNode<string> node30 = node3.AddChild("node30");
    }
}

BONUS
Voir arbre à part entière avec:

  • itérateur
  • recherche
  • Java / C #

Https://github.com/gt4dev/yet-another-tree-structure

34
répondu Grzegorz Dev 2017-01-05 15:12:42

La bibliothèque de Collection Générique C5 généralement excellente possède plusieurs structures de données arborescentes différentes, y compris des ensembles, des sacs et des dictionnaires. Le code Source est disponible si vous souhaitez étudier leurs détails d'implémentation. (J'ai utilisé des collections C5 dans le code de production avec de bons résultats, bien que je n'ai utilisé aucune des structures arborescentes spécifiquement.)

21
répondu McKenzieG1 2008-09-15 21:01:58

Voir http://quickgraph.codeplex.com/

QuickGraph fournit des structures de données et des algorithmes génériques pour.Net 2.0 et versions ultérieures. QuickGraph est livré avec des algorithmes tels que profondeur première seach, souffle première recherche, a * Recherche, chemin le plus court, K-chemin le plus court, débit maximum, minimum spanning tree, ancêtres les moins communs, etc... QuickGraph prend en charge MSAGL, GLEE et Graphviz pour rendre les graphiques, la sérialisation en GraphML, etc...

10
répondu nietras 2010-01-25 15:34:10

Si vous souhaitez écrire le vôtre, vous pouvez commencer par ce document en six parties détaillant l'utilisation efficace des structures de données C# 2.0 et comment analyser votre implémentation de structures de données en C#. Chaque article présente des exemples et un programme d'installation avec des échantillons que vous pouvez suivre.

"Un examen approfondi des Structures de données en utilisant C # 2.0" {[4] } par Scott Mitchell

7
répondu user7116 2008-09-15 21:11:34

J'ai une petite extension aux solutions.

En utilisant une déclaration Générique récursive et une sous-classe dérivante, vous pouvez mieux vous concentrer sur votre cible réelle.

Notez que c'est différent d'une implémentation non générique, vous n'avez pas besoin de lancer 'node' dans 'NodeWorker'.

Voici mon exemple:

public class GenericTree<T> where T : GenericTree<T> // recursive constraint  
{
  // no specific data declaration  

  protected List<T> children;

  public GenericTree()
  {
    this.children = new List<T>();
  }

  public virtual void AddChild(T newChild)
  {
    this.children.Add(newChild);
  }

  public void Traverse(Action<int, T> visitor)
  {
    this.traverse(0, visitor);
  }

  protected virtual void traverse(int depth, Action<int, T> visitor)
  {
    visitor(depth, (T)this);
    foreach (T child in this.children)
      child.traverse(depth + 1, visitor);
  }
}

public class GenericTreeNext : GenericTree<GenericTreeNext> // concrete derivation
{
  public string Name {get; set;} // user-data example

  public GenericTreeNext(string name)
  {
    this.Name = name;
  }
}

static void Main(string[] args)  
{  
  GenericTreeNext tree = new GenericTreeNext("Main-Harry");  
  tree.AddChild(new GenericTreeNext("Main-Sub-Willy"));  
  GenericTreeNext inter = new GenericTreeNext("Main-Inter-Willy");  
  inter.AddChild(new GenericTreeNext("Inter-Sub-Tom"));  
  inter.AddChild(new GenericTreeNext("Inter-Sub-Magda"));  
  tree.AddChild(inter);  
  tree.AddChild(new GenericTreeNext("Main-Sub-Chantal"));  
  tree.Traverse(NodeWorker);  
}  

static void NodeWorker(int depth, GenericTreeNext node)  
{                                // a little one-line string-concatenation (n-times)
  Console.WriteLine("{0}{1}: {2}", String.Join("   ", new string[depth + 1]), depth, node.Name);  
}  
6
répondu Erik Nagel 2012-11-28 17:06:30

Essayez cet exemple simple.

public class TreeNode<TValue>
{
    #region Properties
    public TValue Value { get; set; }
    public List<TreeNode<TValue>> Children { get; private set; }
    public bool HasChild { get { return Children.Any(); } }
    #endregion
    #region Constructor
    public TreeNode()
    {
        this.Children = new List<TreeNode<TValue>>();
    }
    public TreeNode(TValue value)
        : this()
    {
        this.Value = value;
    }
    #endregion
    #region Methods
    public void AddChild(TreeNode<TValue> treeNode)
    {
        Children.Add(treeNode);
    }
    public void AddChild(TValue value)
    {
        var treeNode = new TreeNode<TValue>(value);
        AddChild(treeNode);
    }
    #endregion
}
4
répondu Berezh 2013-02-27 00:10:53

Je crée une classe de noeud qui pourrait être utile pour d'autres personnes. La classe a des propriétés comme:

  • Enfants
  • Ancêtres
  • Descendants
  • frères et sœurs
  • Niveau du noeud
  • Parent
  • Racine
  • etc.

Il y a aussi la possibilité de convertir une liste plate d'éléments avec un Id et un ParentId en arbre. Les nœuds contiennent une référence à la fois aux enfants et au parent, ce qui rend les nœuds itératifs assez rapide.

2
répondu Alex Siepman 2013-08-01 13:32:58

Parce qu'il n'est pas mentionné, je voudrais que vous attiriez l'attention sur le code-base. NET maintenant publié: en particulier le code pour un {[0] } qui implémente un arbre rouge-noir:

Https://github.com/Microsoft/referencesource/blob/master/System/compmod/system/collections/generic/sortedset.cs

Il s'agit cependant d'une structure arborescente équilibrée. Donc, ma réponse est plus une référence à ce que je crois être la seule structure arborescente native dans la bibliothèque. net core.

2
répondu Meirion Hughes 2016-01-27 09:51:56

J'ai complété le code que @Berezh a partagé.

  public class TreeNode<T> : IEnumerable<TreeNode<T>>
    {

        public T Data { get; set; }
        public TreeNode<T> Parent { get; set; }
        public ICollection<TreeNode<T>> Children { get; set; }

        public TreeNode(T data)
        {
            this.Data = data;
            this.Children = new LinkedList<TreeNode<T>>();
        }

        public TreeNode<T> AddChild(T child)
        {
            TreeNode<T> childNode = new TreeNode<T>(child) { Parent = this };
            this.Children.Add(childNode);
            return childNode;
        }

        public IEnumerator<TreeNode<T>> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }
    }
    public class TreeNodeEnum<T> : IEnumerator<TreeNode<T>>
    {

        int position = -1;
        public List<TreeNode<T>> Nodes { get; set; }

        public TreeNode<T> Current
        {
            get
            {
                try
                {
                    return Nodes[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }


        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }


        public TreeNodeEnum(List<TreeNode<T>> nodes)
        {
            Nodes = nodes;
        }

        public void Dispose()
        {
        }

        public bool MoveNext()
        {
            position++;
            return (position < Nodes.Count);
        }

        public void Reset()
        {
            position = -1;
        }
    }
2
répondu Ashkan Sirous 2016-07-24 18:46:50

Voici un Arbre

public class Tree<T> : List<Tree<T>>
{
    public  T Data { get; private set; }

    public Tree(T data)
    {
        this.Data = data;
    }

    public Tree<T> Add(T data)
    {
        var node = new Tree<T>(data);
        this.Add(node);
        return node;
    }
}

Vous pouvez même utiliser des initialiseurs:

    var tree = new Tree<string>("root")
    {
        new Tree<string>("sample")
        {
            "console1"
        }
    };
2
répondu Visar 2016-12-29 07:48:16

La plupart des arbres sont formés par les données que vous traitez.

Dites que vous avez une classe person qui inclut les détails de quelqu'un parents, préféreriez-vous avoir la structure arborescente dans le cadre de votre "classe de domaine", ou utilisez une classe d'arborescence distincte contenant des liens vers votre personne s'oppose? Pensez à une opération simple comme obtenir tout le grandchildren de person, ce code dans le person classe, ou l'utilisateur de la classe person doit-il connaître un séparé de l'arbre de classe?

Un autre exemple est un arbre d'analyse dans un compilateur ...

Ce que ces deux exemples montrent, c'est que le concept d'arbre fait partie du domaine des données et que l'utilisation d'un arbre général distinct double au moins le nombre d'objets créés et rend l'API plus difficile à programmer.

Ce que nous voulons, c'est un moyen de réutiliser les opérations d'arborescence standard, sans avoir à les ré-implémenter pour tous les arbres, tout en n'ayant pas pour utiliser une classe d'arborescence standard. Boost a essayé de résoudre ce type de problème pour C++, mais je n'ai pas encore vu d'effet pour.NET s'adapter.

1
répondu Ian Ringrose 2016-04-11 11:00:14

J'ai ajouté une solution complète et un exemple en utilisant la classe NTree ci-dessus, a également ajouté la méthode "AddChild"...

    public class NTree<T>
    {
        public T data;
        public LinkedList<NTree<T>> children;

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

        public void AddChild(T data)
        {
            var node = new NTree<T>(data) { Parent = this };
            children.AddFirst(node);
        }

        public NTree<T> Parent { get; private set; }

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

        public void Traverse(NTree<T> node, TreeVisitor<T> visitor, string t, ref NTree<T> r)
        {
            visitor(node.data, node, t, ref r);
            foreach (NTree<T> kid in node.children)
                Traverse(kid, visitor, t, ref r);
        }
    }
    public static void DelegateMethod(KeyValuePair<string, string> data, NTree<KeyValuePair<string, string>> node, string t, ref NTree<KeyValuePair<string, string>> r)
    {
        string a = string.Empty;
        if (node.data.Key == t)
        {
            r = node;
            return;
        }
    }

En utilisant

 NTree<KeyValuePair<string, string>> ret = null;
 tree.Traverse(tree, DelegateMethod, node["categoryId"].InnerText, ref ret);
1
répondu Dmitry 2017-04-13 05:09:47

Voici mon propre:

class Program
{
    static void Main(string[] args)
    {
        var tree = new Tree<string>()
            .Begin("Fastfood")
                .Begin("Pizza")
                    .Add("Margherita")
                    .Add("Marinara")
                .End()
                .Begin("Burger")
                    .Add("Cheese burger")
                    .Add("Chili burger")
                    .Add("Rice burger")
                .End()
            .End();

        tree.Nodes.ForEach(p => PrintNode(p, 0));
        Console.ReadKey();
    }

    static void PrintNode<T>(TreeNode<T> node, int level)
    {
        Console.WriteLine("{0}{1}", new string(' ', level * 3), node.Value);
        level++;
        node.Children.ForEach(p => PrintNode(p, level));
    }
}

public class Tree<T>
{
    private Stack<TreeNode<T>> m_Stack = new Stack<TreeNode<T>>();

    public List<TreeNode<T>> Nodes { get; } = new List<TreeNode<T>>();

    public Tree<T> Begin(T val)
    {
        if (m_Stack.Count == 0)
        {
            var node = new TreeNode<T>(val, null);
            Nodes.Add(node);
            m_Stack.Push(node);
        }
        else
        {
            var node = m_Stack.Peek().Add(val);
            m_Stack.Push(node);
        }

        return this;
    }

    public Tree<T> Add(T val)
    {
        m_Stack.Peek().Add(val);
        return this;
    }

    public Tree<T> End()
    {
        m_Stack.Pop();
        return this;
    }
}

public class TreeNode<T>
{
    public T Value { get; }
    public TreeNode<T> Parent { get; }
    public List<TreeNode<T>> Children { get; }

    public TreeNode(T val, TreeNode<T> parent)
    {
        Value = val;
        Parent = parent;
        Children = new List<TreeNode<T>>();
    }

    public TreeNode<T> Add(T val)
    {
        var node = new TreeNode<T>(val, this);
        Children.Add(node);
        return node;
    }
}

Sortie:

Fastfood
   Pizza
      Margherita
      Marinara
   Burger
      Cheese burger
      Chili burger
      Rice burger
1
répondu moien 2018-08-25 06:46:08

Si vous allez à l'affichage de cet arbre sur l'interface graphique, vous pouvez utiliser TreeView et TreeNode. (Je suppose que techniquement vous pouvez créer un TreeNode sans le mettre sur une interface graphique, mais il a plus de frais généraux qu'une simple implémentation de TreeNode locale.)

0
répondu Denise Skidmore 2016-02-04 18:18:56

Voici mon implémentation DE BST

class BST
{
    public class Node
    {
        public Node Left { get; set; }
        public object Data { get; set; }
        public Node Right { get; set; }

        public Node()
        {
            Data = null;
        }

        public Node(int Data)
        {
            this.Data = (object)Data;
        }

        public void Insert(int Data)
        {
            if (this.Data == null)
            {
                this.Data = (object)Data;
                return;
            }
            if (Data > (int)this.Data)
            {
                if (this.Right == null)
                {
                    this.Right = new Node(Data);
                }
                else
                {
                    this.Right.Insert(Data);
                }
            }
            if (Data <= (int)this.Data)
            {
                if (this.Left == null)
                {
                    this.Left = new Node(Data);
                }
                else
                {
                    this.Left.Insert(Data);
                }
            }
        }

        public void TraverseInOrder()
        {
            if(this.Left != null)
                this.Left.TraverseInOrder();
            Console.Write("{0} ", this.Data);
            if (this.Right != null)
                this.Right.TraverseInOrder();
        }
    }

    public Node Root { get; set; }
    public BST()
    {
        Root = new Node();
    }
}
-1
répondu 2014-04-18 08:05:43

Si vous avez besoin d'une implémentation de structure de données arborescente enracinée qui utilise moins de mémoire, vous pouvez écrire votre classe Node comme suit (implémentation C++):

class Node {
       Node* parent;
       int item; // depending on your needs

       Node* firstChild; //pointer to left most child of node
       Node* nextSibling; //pointer to the sibling to the right
}
-3
répondu Jake 2012-05-15 03:18:30