È strano se i nodi di una lista collegata contengono riferimenti all'oggetto List?

3

È come, voglio chiamare .moveToBefore(Node) su un oggetto Node e fare in modo che il nodo si rilasci prima che il nodo sia passato.

Il problema sorge se il nodo passato è il nodo principale. L'oggetto Elenco continuerà a tornare alla vecchia testa dove la vecchia testa seguirà effettivamente la nuova testa più in basso nella catena.

Immagino che questo possa essere risolto facilmente se i nodi hanno un riferimento all'oggetto List. Quindi, vuoi sapere se ci sono degli svantaggi se gli oggetti nodo in un'implementazione Elenco collegato hanno un riferimento al suo oggetto "Elenco" principale.

    
posta Omran Jamal 09.01.2016 - 14:56
fonte

3 risposte

4

Il potenziale problema con la classe Node di conoscere e utilizzare direttamente la classe List è che crei una dipendenza circolare tra Node e List .

Le dipendenze circolari possono essere accettabili se sono attentamente contenute per assicurarti di non finire accidentalmente a rendere tutte le tue classi dipendenti l'una dall'altra. Questo particolare esempio è probabilmente molto facile da contenere e difficilmente "infetterà" il resto delle tue classi, quindi non lo escluderei come progetto potenzialmente valido. Ma potrebbe ancora causare problemi nel mantenere la Lista stessa, poiché in linea di principio significa che non puoi mai cambiare qualsiasi su List senza verificare che non stai rompendo Node nel processo. Ad esempio, cosa succede se vuoi implementare l'operazione di splicing per la tua classe List ? Se il tuo Node s contiene tutti riferimenti al List in cui si trovano, devi aggiornare tutti questi riferimenti, il che significa un codice leggermente più complicato, e la giuntura finirebbe per prendere O (n) invece di O (1 volta. E, se tu non fossi coscientemente consapevole della dipendenza circolare, potresti anche non aver realizzato che dovevi aggiornare quei riferimenti (immagina solo i tipi di bug che potrebbero portare a).

Per questo motivo, per impostazione predefinita avrei impostato List.moveBefore(node1, node2) a meno che non avessi un motivo valido per mettere quel metodo sulla classe Node . Ma se hai una tale ragione, va bene se tieni a mente che non puoi più apportare nessuna modifiche a List o Node senza controllare le implementazioni di entrambe le classi per cose che potrebbe rompersi.

    
risposta data 09.01.2016 - 15:37
fonte
4

L'ovvio svantaggio sarebbe che un nodo non può appartenere a più di una lista. Il fatto che i nodi possano appartenere a più di una lista è in genere fondamentale nell'implementazione di strutture di dati persistenti attraverso la condivisione strutturale.

Quindi, facendo conoscere al nodo la lista, è praticamente impossibile rendere persistente la lista, almeno usando le tecniche attualmente conosciute per farlo. Che si tratti o meno di una restrizione con cui puoi convivere, dipende dalle tue esigenze.

    
risposta data 09.01.2016 - 16:01
fonte
1

Per me il problema più grande è:

  1. Aumentare le dimensioni di un nodo elenco con un puntatore (o riferimenti) aggiuntivo per SLL, 3 per DLL. Questo potrebbe non influire molto sul rendimento se si sta solo assegnando sporadicamente i nodi elenco su tutto lo spazio di memoria, poiché a quel punto non sarebbe importante molto a meno che il nodo fosse solo attorno a 64 byte, ad es. allineato a 64 byte, e il puntatore extra lo rendeva oltre 64 byte, nel qual caso dovevamo raddoppiare i nostri errori di cache. Se stai utilizzando un'attenta strategia di allocazione, più piccolo è il nodo elenco, migliore per l'attraversamento sequenziale e un puntatore in più potrebbe essere un enorme aumento della dimensione relativa solo dall'elenco POV.
  2. Il più grande vantaggio nel mondo reale degli elenchi concatenati, anche nelle aree critiche per le prestazioni, è spesso la capacità di spostare nodi da una lista a un'altra, dividere le liste in due liste e unirle in tempo costante, ecc. scambio per semplificare qualcosa che non è così complesso.
  3. Sono stati citati riferimenti circolari e questo potrebbe essere un problema in alcune lingue. Se è come C o C ++, sarebbe un backpointer della lista che non influenzerebbe la raccolta in alcun modo, i nodi sarebbero comunque distrutti quando richiesto.

Quello che consiglio è di smettere di provare a mettere l'implementazione della lista collegata in un nodo. Crea un nodo dati grezzi, un dettaglio di implementazione privata di un elenco che non si preoccupa di fornire molta logica. La tua vita diventerà generalmente molto più semplice se smetti di provare a suddividere le preoccupazioni di funzionalità troppo tra le strutture collegate e i loro nodi: albero vs nodo dell'albero, elenco collegato o nodo elenco, ad es. Metti tutta la logica nella struttura dati in cui hai tutte le informazioni necessarie per fare tutto (sia head puntatore e nodo). moveToFront dovrebbe essere un metodo di elenco collegato piuttosto che un metodo di nodo di elenco. Con questo, non c'è più bisogno di un backpointer.

class DataStructure
{
public:
    ...

private:
    struct Node
    {
        // Don't put functionality here except maybe a ctor/dtor at 
        // most if you can avoid it. The distribution of concerns should 
        // this node have functionality will often make things more 
        // complex rather than simpler, and also have you reaching for 
        // backpointers quickly, potentially wanting to waste memory
        // and increase the amount of state to maintain superfluously
        // (more state usually means more potential for bugs) just
        // because we want to put functions into this Node class.
    };
    ...
    Node* head_or_root;
};

Un'altra cosa da non sottovalutare è che non c'è un lavoro molto più intelligente che moveToFront possa fare oltre a erase e push to front , quindi potrebbe non valere la pena di avere la funzione extra, a parte una cosa di comodo .

Cioè, a meno che ciò non sia inevitabile, dove i client avrebbero solo un nodo a cui accedono da uno spazio persistente precedentemente memorizzato nella cache, ad es., e nessun accesso alla lista collegata. Se hanno accesso alla lista collegata, allora il modo più semplice è semplicemente evitare di mettere logicamente la logica nei tuoi nodi e renderli privati, tipi di dati C-style struct di cose.

Anche in questo caso spesso semplificherete la vita in questo caso per richiedere semplicemente ai clienti di memorizzare un riferimento a entrambi nodo e lista collegata, invece di sempre memorizzare un backpointer in un nodo (che potrebbe non essere necessario per la maggior parte delle operazioni, facendoci sprecare memoria spesso in luoghi che non ne beneficiano).

Un'altra cosa è che dal momento che Node modella i dettagli di implementazione di un elenco collegato, se puoi evitare di esporre direttamente Node riferimenti e invece fornisci un qualche handle indiretto, come Iterator , ridurrà il rischio di problemi.

    
risposta data 09.01.2016 - 22:54
fonte

Leggi altre domande sui tag