Struttura dei dati dell'albero in C #


248

Stavo cercando una struttura di dati ad albero o grafico in C # ma suppongo che non ce ne fosse uno fornito. Un esame approfondito delle strutture dati utilizzando C # 2.0 spiega un po 'il perché. Esiste una comoda libreria che viene comunemente utilizzata per fornire questa funzionalità? Forse attraverso un modello di strategia per risolvere i problemi presentati nell'articolo.

Mi sento un po 'sciocco a implementare il mio albero, proprio come farei con la mia ArrayList.

Voglio solo un albero generico che può essere sbilanciato. Pensa a un albero di directory. C5 sembra elegante, ma le loro strutture ad albero sembrano essere implementate come alberi rosso-neri bilanciati più adatti alla ricerca che rappresentare una gerarchia di nodi.


2
Alberi un po 'più estremi: stackoverflow.com/questions/196294/… ;-)
Tuomas Hietanen,

C'è qualche motivo per cui non è possibile includere un TreeView nel progetto e utilizzarlo? Non c'è motivo di mostrarlo effettivamente a un utente. Naturalmente ci sono diverse forme di progetti quando questa non è un'opzione. Si possono sempre creare nuove classi che ereditano dall'esempio TreeNode se è necessaria una complessità speciale?
Semplicemente G.

9
Considererei una cattiva idea importare un'intera libreria UI per un albero molto semplice.
stimola il

1
Potresti motivare? Non è più come un vero e proprio requisito di spazio su disco rigido? Clumsy? Come ho detto prima, posso capire che questa non è una soluzione per un software specializzato o qualcosa senza un'interfaccia utente esistente. Sono un programmatore pigro, se riesco a ottenere una struttura gratuitamente va tutto bene. E una biblioteca esistente ha molto gratis, si può trovare molto codice da persone che lo hanno usato per molte cose.
Semplicemente G.

Non sto litigando, voglio solo conoscere il tuo ragionamento.
Semplicemente G.

Risposte:


155

Il mio miglior consiglio sarebbe che non esiste una struttura di dati ad albero standard perché ci sono così tanti modi per implementarla che sarebbe impossibile coprire tutte le basi con una soluzione. Più una soluzione è specifica, meno è probabile che sia applicabile a un determinato problema. Mi annoio anche con LinkedList: cosa succede se desidero un elenco di collegamenti circolari?

La struttura di base che dovrai implementare sarà una raccolta di nodi, e qui ci sono alcune opzioni per iniziare. Supponiamo che la classe Node sia la classe base dell'intera soluzione.

Se devi solo navigare lungo l'albero, una classe Node ha bisogno di un Elenco di figli.

Se è necessario spostarsi nella struttura ad albero, la classe Node necessita di un collegamento al nodo principale.

Costruisci un metodo AddChild che si occupa di tutte le minuzie di questi due punti e di qualsiasi altra logica aziendale che deve essere implementata (limiti figlio, ordinamento dei figli, ecc.)


5
personalmente non mi dispiacerebbe aggiungere una sorta di albero binario auto-bilanciante da aggiungere alla libreria in quanto si tratta di un lavoro extra rispetto al semplice utilizzo di un elenco adiacente.
jk.

8
@jk Credo che SortedDictionary e SortedSet siano costruiti sopra alberi rossi / neri, quindi l'utilizzo di questi dovrebbe funzionare.
jonp,

Dai un'occhiata al modello composito ;-) Esattamente quello che stai cercando
Nicolas Voron,

119
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);
    }
}

Semplice implementazione ricorsiva ... <40 righe di codice ... Devi solo mantenere un riferimento alla radice dell'albero al di fuori della classe, o racchiuderlo in un'altra classe, forse rinominarlo in TreeNode ??


22
In questo caso, in C # in ogni caso, si potrebbe evitare di scrivere il proprio delegato e utilizzare il pre-made Action<T>delegato: public void traverse(NTree<T> node, Action<T> visitor). Azione <> 's firma è: void Action<T>( T obj ). Esistono anche versioni da 0 a 4 parametri diversi. C'è anche un delegato analogo per le funzioni chiamate Func<>.
Benny Jobigan,

2
come definirei questo delegato?
Stranamente il

3
cambiare il metodo di attraversamento in modo statico o eventualmente spostarlo per nascondere la natura ricorsiva sarebbe una buona idea, ma è semplice da attraversare: creare un metodo con la firma del delegato, cioè per un albero di ints: void my_visitor_impl (int datum) - rendilo statico se necessario, crea un'istanza di un delgate: TreeVisitor <int> my_visitor = my_visitor_impl; e quindi invoca il nodo radice o la classe NTree se lo rendi statico: NTree <int> .traverse (my_tree, my_visitor)
Aaron Gage

10
Fare in modo che addChild () restituisca il NTree che ha aggiunto lo renderebbe più piacevole per l'aggiunta di dati a un albero. (A meno che non mi manchi un modo astuto per costruire un albero con questo, senza fare affidamento sui dettagli di implementazione che un bambino appena aggiunto == getChild (1)?)
Rory

1
Penso che questa affermazione --i == 0funzionerà solo in un singolo caso? È vero. Questo mi ha fatto confondere
Waseem Ahmad Naeem il

57

Ecco il mio, che è molto simile a quello di Aaron Gage , solo un po 'più convenzionale, secondo me. Per i miei scopi, non ho riscontrato problemi di prestazioni con List<T>. Sarebbe abbastanza facile passare a un LinkedList se necessario.


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()));
        }
    }
}

perché la tua proprietà Value è esposta quando la imposti nel costruttore? che lo lascia aperto alla manipolazione DOPO che l'hai già impostato tramite il costruttore, giusto? Dovrebbe essere un set privato?
Positivo

Certo, perché non renderlo immutabile? Modificato.
Ronnie Overby,

Grazie! Mi è abbastanza piaciuto non dover scrivere il mio. (Non riesco ancora a credere che non esista in modo nativo. Ho sempre pensato che .net, o almeno .net 4.0, avesse tutto .)
neminem

3
Mi è piaciuta questa soluzione. Ho anche scoperto che dovevo inserire, ho aggiunto il seguente metodo per farlo. public TreeNode<T> InsertChild(TreeNode<T> parent, T value) { var node = new TreeNode<T>(value) { Parent = parent }; parent._children.Add(node); return node; } var five = myTree.AddChild(5); myTree.InsertChild(five, 55);
JabberwockyDecompiler

48

Ancora un'altra struttura ad albero:

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
}

Esempio di utilizzo:

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
Vedi l'albero a tutti gli effetti con:

  • iteratore
  • ricerca
  • Java / C #

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


Come utilizzo la ricerca nell'esempio di codice? Da dove nodeviene Significa che devo scorrere sopra l'albero per utilizzare il codice di ricerca?
BadmintonCat

@GrzegorzDev Forse -1 perché non implementa tutti i IEnumerable<>membri, quindi non viene compilato.
Uwe Keim,

1
@UweKeim Ottimo lavoro, la prossima volta prova a utilizzare il codice con gli utilizzi effettivi.
szab.kel,

l'unico problema che vedo è che non verrà serializzato correttamente con JsonConvert di base poiché implementa IEnumerable <>
Rakiah,

22

La libreria di raccolta generica C5 generalmente eccellente ha diverse strutture dati basate su alberi, tra cui set, borse e dizionari. Il codice sorgente è disponibile se si desidera studiare i dettagli dell'implementazione. (Ho usato le raccolte C5 nel codice di produzione con buoni risultati, anche se non ho usato nessuna delle strutture ad albero in modo specifico.)


7
Non so se forse le cose sono cambiate, ma in questo momento il libro è liberamente disponibile per il download in formato PDF dal sito C5.
Oskar,

4
La mancanza di documentazione non è più una preoccupazione in quanto c'è un pdf di 272 pagine che completa la biblioteca ... Non posso commentare la qualità del codice, ma a giudicare dalla qualità del documento, non vedo davvero l'ora di approfondire questa sera!
Florian Doyon,

2
Da quello che ho capito, questa libreria C5 non ha alberi, ma solo alcune strutture di dati derivati ​​da alberi.
Roim

10

Vedi http://quickgraph.codeplex.com/

QuickGraph fornisce algoritmi e algoritmi grafici generici diretti / non indirizzati per .Net 2.0 e versioni successive. QuickGraph viene fornito con algoritmi come profondità prima ricerca, respiro prima ricerca, ricerca A *, percorso più breve, percorso k-più breve, flusso massimo, albero di spanning minimo, antenati meno comuni, ecc ... QuickGraph supporta MSAGL, GLEE e Graphviz per renderizza i grafici, serializzazione su GraphML, ecc ...


8

Se desideri scriverne uno tuo, puoi iniziare con questo documento in sei parti che descrive in dettaglio l'uso efficace delle strutture di dati di C # 2.0 e come analizzare la tua implementazione delle strutture di dati in C #. Ogni articolo contiene esempi e un programma di installazione con esempi che puoi seguire insieme.

"Un esame approfondito delle strutture dati utilizzando C # 2.0" di Scott Mitchell


7

Ho una piccola estensione delle soluzioni.

Usando una dichiarazione generica ricorsiva e una sottoclasse derivante puoi concentrarti meglio sul tuo obiettivo reale.

Nota, è diverso da un'implementazione non generica, non è necessario eseguire il cast di "nodo" in "NodeWorker".

Ecco il mio esempio:

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);  
}  

cos'è la profondità e da dove e come si ottiene?
Positivo

@ WeDoTDD.com guardando la sua classe vedi Traverse che lo dichiara come 0 per iniziare dal nodo radice, quindi usa il metodo traverse aggiungendo a quello int ogni iterazione.
Edward,

Come cercheresti nell'intero albero un nodo particolare?
mattpm

6

Ecco il mio:

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;
    }
}

Produzione:

Fastfood
   Pizza
      Margherita
      Marinara
   Burger
      Cheese burger
      Chili burger
      Rice burger

4

Prova questo semplice esempio.

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
}

2

Creo una classe Node che potrebbe essere utile per altre persone. La classe ha proprietà come:

  • Bambini
  • antenati
  • discendenti
  • fratelli
  • Livello del nodo
  • Genitore
  • Radice
  • Eccetera.

C'è anche la possibilità di convertire un elenco semplice di elementi con un ID e un ParentId in un albero. I nodi contengono un riferimento sia ai figli che al genitore, in modo da rendere i nodi iterativi abbastanza veloci.


2

Perché non è menzionato, vorrei che tu attirassi l'attenzione sulla base di codice .net ora rilasciata: in particolare il codice per un SortedSetche implementa un Red-Black-Tree:

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

Questa è, tuttavia, una struttura ad albero bilanciata. Quindi la mia risposta è più un riferimento a quella che credo sia l'unica struttura ad albero nativa nella libreria core .net.


2

Ho completato il codice condiviso da @Berezh.

  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;
        }
    }

Buon design. Tuttavia, non sono sicuro che un nodo "sia" una sequenza del suo nodo figlio. Considererei quanto segue: un nodo "ha" zero o più nodi figlio, quindi un nodo non deriva da una sequenza di nodi figlio, ma è un'aggregazione (composizione?)
Dei

2

Ecco un albero

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;
    }
}

Puoi anche usare gli inizializzatori:

    var tree = new Tree<string>("root")
    {
        new Tree<string>("sample")
        {
            "console1"
        }
    };

1

La maggior parte degli alberi è formata dai dati che stai elaborando.

Supponi di avere una personclasse che includa dettagli di qualcuno parents, preferiresti avere la struttura ad albero come parte della tua "classe di dominio" o utilizzare una classe ad albero separata che contenesse collegamenti ai tuoi oggetti personali? Pensa a un'operazione semplice come ottenere tutte le grandchildrenlettere a person, questo codice dovrebbe essere nella person classe o l'utente della personclasse dovrebbe conoscere una classe ad albero separata?

Un altro esempio è un albero di analisi in un compilatore ...

Ciò che entrambi questi esempi mostrano è che il concetto di albero fa parte del dominio dei dati e l'utilizzo di un albero per scopi generali separato raddoppia almeno il numero di oggetti creati e rende l'API più difficile da riprogrammare.

Ciò che vogliamo è un modo per riutilizzare le operazioni sugli alberi standard, senza doverle implementare nuovamente per tutti gli alberi, mentre allo stesso tempo, non è necessario utilizzare una classe di alberi standard. Boost ha provato a risolvere questo tipo di problema per C ++, ma non ho ancora visto alcun effetto per .NET adattato.


@Puchacz, scusa se ho 15 anni di dati su C ++, dai un'occhiata a Boost and Templates, dopo qualche debole studio puoi capirli. Il potere ha costi di apprendimento elevati !!
Ian Ringrose,

1

Ho aggiunto soluzione completa ed esempio utilizzando la classe NTree sopra, anche aggiunto il metodo "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;
        }
    }

utilizzando

 NTree<KeyValuePair<string, string>> ret = null;
 tree.Traverse(tree, DelegateMethod, node["categoryId"].InnerText, ref ret);

La traversata dovrebbe forse essere un metodo statico? Sembra molto imbarazzante come un metodo di istanza che si passa da solo
Sinaesthetic

0

Ecco la mia implementazione di 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();
    }
}

0

Se stai per visualizzare questo albero nella GUI, puoi usare TreeView e TreeNode . (Suppongo che tecnicamente sia possibile creare un TreeNode senza inserirlo in una GUI, ma ha un overhead maggiore rispetto a una semplice implementazione TreeNode nostrana.)


-4

Nel caso in cui sia necessaria un'implementazione della struttura dei dati dell'albero rooted che utilizza meno memoria, è possibile scrivere la classe Node come segue (implementazione 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
}

12
Pubblicare codice C ++ su una domanda specifica per C # non è la migliore idea, Jake. Soprattutto uno che include puntatori. Sai che i puntatori vengono cacciati senza pietà in C #, giusto? : p
ThunderGr

2
@ThunderGr che non è giusto. Rispondere in C # sarebbe stato meglio, ma quei puntatori C ++ possono essere compresi dagli altoparlanti C # come riferimenti (sono meno sicuri, ok). Dopo che David Boike, Aaron Gage, Ronnie Overby, Grzegorz Dev, Berezh ed Erik Nagel hanno tutti suggerito sostanzialmente la stessa struttura di dati con differenze minori solo nell'espressione, Jake ha suggerito di scomporre l'elenco collegato producendo una struttura più semplice con un solo tipo di nodo e navigabilità tra fratelli. Non esprimere la tua antipatia per il C ++ votando verso il basso una risposta costruttiva.
migle

3
@migle Non ho votato in negativo la risposta (non ho neanche votato). E non mi piace il C ++. Ho visto che la risposta è stata declassata senza che nessuno suggerisse a Jake perché e come avrebbe migliorato la sua risposta. Non si tratta di "stare meglio". La domanda è taggata solo per C #. Non è consigliabile pubblicare risposte in un'altra lingua rispetto al tag e alcune persone effettueranno il downgrade.
ThunderGr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.