Perché la cancellazione di solito è molto più difficile da implementare rispetto all'inserimento in molte strutture di dati?


33

Riesci a pensare a qualche motivo specifico per cui la cancellazione è di solito significativamente più difficile da implementare rispetto all'inserimento per molte (la maggior parte?) Strutture di dati?

Esempio rapido: elenchi collegati. L'inserimento è banale, ma la cancellazione ha alcuni casi speciali che lo rendono significativamente più difficile. Gli alberi di ricerca binari autobilanciati come AVL e Red-black sono esempi classici di implementazione dolorosa dell'eliminazione.

Vorrei dire che ha a che fare con il modo in cui la maggior parte della gente pensa: è più facile per noi definire le cose in modo costruttivo, il che porta piacevolmente a facili inserimenti.


4
Che dire pop, extract-min?
coredump,

5
"Più difficile da implementare" è più una questione di psicologia (cognizione e punti di forza e di debolezza della mente umana) che di programmazione (proprietà di strutture di dati e algoritmi).
uscita

1
Come ritengo ci sia allusione a coredump, gli stack dovrebbero essere almeno altrettanto facili da eliminare come add (per uno stack supportato da array, il popping è solo un decremento del puntatore [1] mentre il push potrebbe richiedere un'intera copia dell'array se si raggiunge il massimo array). Inoltre ci sono alcuni casi d'uso in cui si presume che gli inserimenti saranno frequenti e le eliminazioni meno, ma sarebbe una struttura di dati molto magica in cui il numero di eliminazioni supera gli inserimenti. [1] Probabilmente dovresti anche annullare il riferimento ora invisibile all'oggetto spuntato per evitare perdite di memoria, che ricordo perché il libro di testo di Liskov non lo fece
Foon,

43
"Cameriere, potresti aggiungere altro maionese a questo sandwich?" "Certo, nessun problema, signore." "Potresti anche rimuovere tutta la senape?" "Uh ......"
cobaltduck,

3
Perché la sottrazione è più complicata dell'aggiunta? Divisione (o scomposizione in fattori primi) più complicata della moltiplicazione? Radici più complicate dell'espiazione?
mu è troppo corto il

Risposte:


69

È più di un semplice stato d'animo; ci sono ragioni fisiche (cioè digitali) per cui la cancellazione è più difficile.

Quando elimini, lasci un buco dove prima c'era qualcosa. Il termine tecnico per l'entropia risultante è "frammentazione". In un elenco collegato, ciò richiede che si "correggi" il nodo rimosso e si distribuisca la memoria che sta utilizzando. Negli alberi binari, provoca uno sbilanciamento dell'albero. Nei sistemi di memoria, la memoria rimane inutilizzata per un po 'se i blocchi appena allocati sono più grandi dei blocchi lasciati indietro dalla cancellazione.

In breve, l'inserimento è più semplice perché puoi scegliere dove inserire. L'eliminazione è più difficile perché non è possibile prevedere in anticipo quale elemento verrà eliminato.


3
La frammentazione non è un problema in cui entrano in gioco puntatori e riferimenti indiretti, sia per la struttura in memoria che per i diagrammi. In memoria, non importa dove esistano i singoli nodi a causa del riferimento indiretto. Per gli elenchi, l'eliminazione di un nodo interno (che è il punto in cui si avrebbe un buco nel diagramma) comporta un numero leggermente inferiore di operazioni rispetto all'inserimento (1 assegnazione del puntatore e 1 assegnazione libera rispetto a 1 e 2 assegnazione del puntatore). Per gli alberi, l'inserimento di un nodo può sbilanciare un albero tanto quanto la cancellazione. Sono i casi limite che causano le difficoltà a cui si riferisce brito, dove la frammentazione non ha importanza.
uscita

12
Non sono d'accordo sul fatto che inserzioni ed eliminazioni differiscano nella prevedibilità. "Patching intorno" a un nodo elenco è esattamente ciò che accade al contrario se si deve inserire lo stesso nodo. Non c'è incertezza in nessuna direzione in nessun punto, e in qualsiasi contenitore senza struttura intrinseca ai suoi elementi (ad es. Un albero binario bilanciato, un array con una stretta relazione tra gli offset degli elementi) non esiste alcun "buco". Pertanto, temo di non sapere di cosa stai parlando qui.
sqykly,

2
Molto interessante, ma direi che mancano gli argomenti. È possibile organizzare strutture di dati in base alla cancellazione semplice / veloce senza problemi. È solo meno comune, molto probabilmente anche meno utile.
luk32,

@sqykly Penso che l'elenco sia stato un esempio di scelta sbagliata perché l'inserimento centrale e la relazione intermedia sono ugualmente difficili. Un caso alloca la memoria in cui l'altro riallocato. Uno apre un buco dove l'altro sigilla un buco. Quindi non tutti i casi sono più complessi di quelli aggiunti.
ydobonebi,

36

Perché tende ad essere più difficile da eliminare che da inserire? Le strutture di dati sono progettate più pensando all'inserimento che alla cancellazione, e giustamente.

Considera questo: per eliminare qualcosa da una struttura di dati, deve essere lì in primo luogo. Quindi devi prima aggiungerlo, nel senso che al massimo hai tante eliminazioni quante sono le inserzioni. Se ottimizzi una struttura di dati per l'inserimento, avrai la garanzia di ottenere almeno lo stesso vantaggio che se fosse stato ottimizzato per l'eliminazione.

Inoltre, a che serve l'eliminazione sequenziale di ciascun elemento? Perché non chiamare semplicemente una funzione che cancella tutto in una volta (possibilmente semplicemente creando una nuova)? Inoltre, le strutture di dati sono molto utili quando contengono effettivamente qualcosa. Quindi, in pratica, il caso di avere tante eliminazioni quante le inserzioni non sarà molto comune.

Quando ottimizzi qualcosa, vuoi ottimizzare le cose che fa di più e che richiedono più tempo. Nell'uso normale, la cancellazione di elementi di una struttura di dati avviene meno frequentemente dell'inserimento.


4
C'è un caso d'uso che posso immaginare. Una struttura di dati che viene preparata per l'inserimento iniziale e quindi il consumo individuale. Certamente è un caso raro, e non molto interessante dal punto di vista algoritmico, perché come hai detto, un'operazione del genere non può dominare l'inserimento asintoticamente. Forse c'è qualche speranza in effetti che l'inserimento batch possa avere un costo ammortizzato abbastanza buono ed essere veloce e semplice per l'eliminazione, quindi avrebbe inserimenti batch complicati ma pratici e eliminazioni individuali semplici e veloci. Certamente un'esigenza pratica molto rara.
luk32,

1
Ummm, penso che un esempio potrebbe essere un vettore ordinato al contrario. È possibile aggiungere un batch kdi elementi piuttosto velocemente: invertire l'ordine di input e unire con il vettore esistente - O(k log k + n). Quindi hai una struttura con un inserimento abbastanza complicato ma consumare gli uelementi migliori è banale e veloce. Basta prendere l'ultimo ue spostare la fine del vettore. Tuttavia, se qualcuno ha mai bisogno di una cosa del genere, sarò dannato. Spero che questo rafforzi almeno il tuo argomento.
luk32,

Non dovresti voler ottimizzare per il modello di utilizzo medio piuttosto che quello che fai di più?
Shiv,

Una semplice coda di lavoro FIFO cercherà in genere di essere vuota per la maggior parte del tempo. Una coda ben progettata sarà ben ottimizzata (ovvero O (1)) sia per gli inserimenti che per le eliminazioni (e una molto buona supporterà anche operazioni simultanee veloci, ma questo è un problema diverso).
Kevin,

6

Non è più difficile.

Con gli elenchi doppiamente collegati, quando si inserisce, si allocerà la memoria e quindi si collegherà con il nodo head o precedente e con la coda o il nodo successivo. Quando si elimina, si verrà scollegati esattamente dallo stesso, quindi si libererà memoria. Tutte queste operazioni sono simmetriche.

Ciò presuppone che in entrambi i casi si disponga del nodo da inserire / eliminare. (E nel caso dell'inserzione, che hai anche il nodo da inserire prima, quindi in un certo senso, l'inserimento potrebbe essere considerato leggermente più complicato.) Se stai cercando di eliminare non avendo il nodo da eliminare, ma il payload del nodo, quindi ovviamente dovrai prima cercare nell'elenco il payload, ma non è un difetto di eliminazione, vero?

Con alberi bilanciati, lo stesso vale: un albero generalmente ha bisogno di essere equilibrato immediatamente dopo un inserimento e anche immediatamente dopo una cancellazione. È una buona idea provare ad avere solo una routine di bilanciamento e applicarla dopo ogni operazione, indipendentemente dal fatto che si tratti di un inserimento o di una cancellazione. Se stai cercando di implementare un inserimento che lascia sempre l'albero equilibrato, e anche una cancellazione che lascia sempre l'albero equilibrato, senza che i due condividano la stessa routine di bilanciamento, stai complicando inutilmente la tua vita.

In breve, non vi è alcun motivo per cui uno dovrebbe essere più difficile dell'altro, e se lo stai scoprendo, è in effetti possibile che tu sia vittima della tendenza (molto umana) di trovare più naturale pensare costruttivamente che sottrattivamente, il che significa che potresti implementare la cancellazione in un modo più complicato di quanto debba essere. Ma questo è un problema umano. Da un punto di vista matematico, non vi è alcun problema.


1
Non sono d'accordo. L'algoritmo di eliminazione AVL è più complesso dell'inserimento. Per alcune eliminazioni di nodi potrebbe essere necessario riequilibrare l'intero albero, che in genere viene eseguito in modo ricorsivo ma può anche essere eseguito in modo non ricorsivo. Non è necessario eseguire questa operazione per l'inserimento. Non sono a conoscenza dei progressi dell'algoritmo in cui tale riequilibrio dell'intero albero può essere evitato in tutti i casi.
Dennis,

@Dennis: è possibile che gli alberi AVL seguano l'eccezione anziché la regola.
uscita

@outis IIRC, tutti gli alberi di ricerca bilanciati hanno routine di cancellazione più complicate (rispetto all'inserzione).
Raffaello,

Che dire delle tabelle hash di hashing chiuse ? L'inserimento è (relativamente) semplice, la cancellazione è almeno più difficile da concettualizzare poiché devi sistemare tutto "la cosa che doveva essere all'indice X è attualmente all'indice Y e dobbiamo andare a trovarlo e rimetterlo" problemi.
Kevin,

3

In termini di runtime, esaminando il confronto della complessità temporale delle operazioni della struttura dei dati su Wikipedia, si noti che le operazioni di inserimento ed eliminazione hanno la stessa complessità. L'operazione di eliminazione profilata è la cancellazione per indice, in cui si ha un riferimento all'elemento struttura da eliminare; l'inserimento avviene per articolo. Il tempo di esecuzione più lungo per l'eliminazione in pratica è perché di solito hai un elemento da eliminare e non il suo indice, quindi hai anche bisogno di un'operazione di ricerca. La maggior parte delle strutture di dati nella tabella non richiede una ricerca aggiuntiva per un inserto perché la posizione di posizionamento non dipende dall'elemento o la posizione viene determinata implicitamente durante l'inserimento.

Per quanto riguarda la complessità cognitiva, c'è una risposta alla domanda: casi limite. La cancellazione può avere più di questi rispetto all'inserimento (questo deve ancora essere stabilito nel caso generale). Tuttavia, almeno alcuni di questi casi limite possono essere evitati in alcuni progetti (ad es. Avere un nodo sentinella in un elenco collegato).


2
"La maggior parte delle strutture dati non richiede una ricerca per un inserto." -- ad esempio? Anzi, farei l'affermazione opposta. ("Trova" la posizione di inserimento, che è tanto costosa quanto ritrovare lo stesso elemento più tardi.)
Raffaello,

@Raphael: questa risposta dovrebbe essere letta nel contesto della tabella collegata delle complessità dell'operazione, che non include l'operazione di ricerca come parte dell'eliminazione. In risposta alla tua domanda, ho classificato la struttura per nome comune. Di matrici, elenchi, alberi, tabelle hash, pile, code, cumuli e insiemi, alberi e insiemi richiedono una ricerca per un inserto; gli altri usano un indice non collegato all'elemento (per pile, code e cumuli di base, è esposto solo 1 indice e il rilevamento non è supportato) o lo calcola dall'elemento. I grafici possono andare in entrambi i modi, a seconda di come vengono utilizzati.
uscita

... I tentativi potrebbero essere considerati alberi; tuttavia, se classificato come propria struttura, se c'è un "trovare" durante l'inserimento è più una questione di dibattito, quindi non lo includo. Si noti che l'elenco della struttura dei dati non tiene conto dell'interfaccia rispetto all'implementazione. Inoltre, il modo in cui conti dipende in gran parte da come categorizzi. Vedrò se riesco a pensare a una dichiarazione più obiettiva.
uscita

Devo ammettere che avevo in mente l'interfaccia dizionario / set (come comune in CS). Ad ogni modo, quel tavolo è fuorviante e (iirc) anche sbagliato in diversi punti - Wikipedia, la fossa della disinformazione CS. : /
Raffaello

0

Oltre a tutti i problemi citati, vi è l'integrità referenziale dei dati. Per costruire in modo più appropriato la struttura dei dati come i database in SQL, l'integrità referenziale Oracle è molto importante.
Per essere sicuro di non distruggerlo accidentalmente molte cose diverse inventate.
Ad esempio, a cascata su delete, che non solo elimina ciò che si tenta di eliminare, ma attiva anche la pulizia dei dati correlati.
Questo ripulisce il database dai dati spazzatura e mantiene intatta l'integrità dei dati.
Ad esempio, hai tabelle con genitori e tipi come record correlati nella seconda tabella.
Dove parent è la tabella principale. Se non si dispone di un'integrità referenziale rafforzata in atto, è possibile eliminare qualsiasi record in qualsiasi tabella e in seguito non si saprà come ottenere informazioni complete sulla famiglia poiché sono presenti dati nella tabella figlio e nulla nella tabella padre.
Questo è il motivo per cui il controllo dell'integrità referenziale non consente di eliminare i record dalla tabella padre fino a quando i record dalla tabella figlio non vengono ripuliti.
Ed è per questo che nella maggior parte delle fonti di dati è più difficile eliminare i dati.


Penso che la domanda fosse su strutture in memoria come elenchi collegati, tabelle hash, ecc. Piuttosto che database, ma l'integrità referenziale è un grosso problema anche con le strutture in memoria.
supercat,
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.