Come funziona un elenco di salto?


14

Per un compito a casa, devo capire come funziona un elenco di salto .

Sto programmando da poco più di 2 anni (so che non è così lungo nella realtà), e non ho mai nemmeno sentito parlare di un elenco di salto.

Ho esaminato tutte le guide che riesco a trovare e riesco ancora a malapena a capire come funzionano. Ho anche cercato Code Review per un'implementazione di esempio e ho trovato solo una recensione; e non è nemmeno un'implementazione completa. Ho esaminato l'implementazione del campione fornita dal corso ed è assolutamente atroce. Tra la mancanza di metodi adeguati e nomi di variabili a lettera singola, non ho idea di come funzioni.

Come funziona un elenco di salto? È necessaria la conoscenza di un elenco di salto per comprendere strutture di dati più avanzate?



1
I consigli per l'istruzione sono esplicitamente fuori tema . Dato che si tratta di strutture di dati e non di istruzione, ho modificato la tua domanda per rimuovere quelle parti. Consiglio anche di leggere il link Wikipedia in cui ho modificato e aggiornare la tua domanda con dettagli più specifici su ciò che ancora non capisci.

@Snowman Grazie. L'ho aggiunto solo per evitare commenti come "chiedi al tuo insegnante". Lo terrò a mente per la prossima volta. E hai aggiunto una modifica che cambia la domanda. Alla fine, non sto chiedendo alla gente di spiegare come funzionano, dato che presumo sia offtopico (anche se non sarei contrario a una buona spiegazione). Voglio solo sapere quanto sono importanti da imparare.
Carcigenicate,

1
@Carcigenicate che spiega come funzionano è in realtà più in tema che chiedere se li vedrai nel mondo reale. Possiamo solo indovinare cosa farai e i vari regni. Chiedere se li vediamo nel mondo reale ci sta chiedendo "sì, li vedo e li uso" o "no, non ne ho mai sentito parlare" - che non forniscono risposte buone o utili che altre persone possano leggere.

Risposte:


29

In passato, nella classe delle strutture dati abbiamo imparato come funzionavano gli alberi AVL . Lo avrei avuto in una delle mie lezioni, ma l'istruttore ha detto "non lo userai mai realmente" e invece ci ha fatto imparare 2-3 alberi e b * alberi. Erano giorni in cui la memoria era stretta e i processi erano singolarmente sottoposti a thread. Non hai usato un deque quando un elenco collegato singolarmente avrebbe funzionato altrettanto bene.

L'elenco di salto è molto più comune oggi con più memoria disponibile e concorrenza che rappresentano un problema (non è necessario bloccare molto quando si agisce come scrittore in un elenco di salto, rispetto a tutto con un albero AVL).

Francamente, ora è la mia struttura di dati preferita in quanto è qualcosa che posso facilmente ragionare su come funziona sotto e dove sarà vantaggioso o svantaggioso da usare.

Non avrai bisogno di scriverne uno da zero (a meno che tu non lo ottenga come una domanda di intervista - ma allora hai la stessa probabilità di implementare un albero AVL).

Si sta andando ad avere bisogno di capire il motivo per cui si desidera selezionare un ConcurrentSkipListMapin Java piuttosto che una HashMapo TreeMapo una qualsiasi delle altre implementazioni della mappa.


Per capire come funziona, devi capire come funziona un albero binario. Aspetta, lasciami modificare. Devi capire come funziona un albero binario bilanciato . Senza bilanciare un albero binario, non ottieni alcun vantaggio reale con la sua ricerca.

Diciamo che abbiamo questo albero:

Un albero binario

E inseriamo un '8' in esso. Ora abbiamo:

Un albero binario sbilanciato

E questo non è equilibrato. Quindi, andiamo a fare la magia di bilanciarlo tramite qualche implementazione ...

albero equilibrato

E hai di nuovo un albero equilibrato. Ma è stata molta magia che ho agitato la mano.

Consente di saltare un elenco.

skip list ideale

Questo sembra essere idealizzato. Pochi lo sono, ma mostra la natura equilibrata dell'albero binario che l'ideale skiplist approssima.

Ora, vogliamo inserire un 6 lì dentro. Questo lo sta inserendo proprio come un elenco collegato. Tuttavia, iniziamo dall'alto e scendiamo. I primi punti a 5. È 6> 5? Sì. Ok, ora il top punta fino alla fine, quindi scendiamo in pila (siamo sul 5). Il prossimo è 7. È 6> 7? No. Quindi scendiamo di livello e siamo al livello base, quindi inseriamo 6 a destra del 5.

Lanciamo una moneta: teste che costruiamo, code che rimaniamo. Tails. Non è necessario fare altro.

salta la lista dopo un inserimento

Ora inseriamo 8. 8> 5? Sì. 8> 7? Sì. E ora siamo di nuovo al livello inferiore dopo aver seguito le frecce e lo stack intorno e testiamo 8> 11? No. Quindi inseriamo l'8 a destra del 7.

Lanciamo una moneta: teste che costruiamo, code che rimaniamo. Tails. Non è necessario fare altro.

salta la lista dopo un altro inserimento

Nell'albero bilanciato, ci saremmo tutti preoccupati che l'albero non fosse bilanciato ora. Ma questo non è un albero - è un elenco da saltare. Abbiamo approssimare un albero bilanciato.

Ora inseriamo un 10. Eviterò tutti i confronti.

Lanciamo una moneta: teste che costruiamo, code che rimaniamo. Teste! E capovolgi di nuovo, Heads di nuovo! Capovolgilo di nuovo, ok, c'è una coda. Due livelli sopra l'elenco collegato di base.

salta la lista dopo l'ennesimo inserto

Il vantaggio qui è che ora se vogliamo inserire un 12, possiamo saltare da 5 a 10 senza fare tutti gli altri confronti. Possiamo ignorarli con l'elenco di salto. E non dobbiamo preoccuparci dell'albero bilanciato: la natura probabilistica dell'impilamento lo fa per noi.

Perché è così utile? Perché quando inserisco il 10 posso farlo bloccando la scrittura sui puntatori 5 e 7 e 8 piuttosto che sull'intera struttura. E mentre lo sto facendo, i lettori possono ancora passare attraverso l'elenco di salto senza avere uno stato incoerente. Per un utilizzo simultaneo, è più veloce non dover bloccare. Per iterare sul livello inferiore, è più veloce di un albero (le gioie degli algoritmi BFS e DFS per la navigazione dell'albero - non devi preoccuparti di loro).

Lo incontrerai? Probabilmente lo vedrai in uso in alcuni punti. E poi saprai perché l'autore ha scelto quella implementazione piuttosto che una TreeMapo HashMapper la struttura.

Gran parte di questo è stato preso in prestito dal mio post sul blog: The Skip List


Grazie. Non è nemmeno l'implementazione generale che non capisco; Ottengo la loro somiglianza con i BST. Ho provato a pensare a come lo avrei implementato e il pensiero di gestire tutti i puntatori / riferimenti mi ha costantemente confuso. Forse mi sono lasciato frustrare troppo. Grazie. Proverò a raccoglierlo domani usando la tua risposta come punto di partenza.
Carcigenicato,

2
@Carcigenicato potresti trovare utile anche l'articolo originale che li introduce - Skip Lists: A Probabilistic Alternative to Balanced Trees . È un documento piuttosto comprensibile rispetto alla maggior parte dei documenti accademici che possono andare ben oltre le teste delle persone. La tabella 2 è il motivo per cui li vedrai in uso. A quel fattore temporale per l'inserimento o la cancellazione si aggiunge la complessità di altre soluzioni.

2
Un elenco collegato è "solo un albero degenerato molto sbilanciato". Una lista salta è una sorta di aggiunta parziale di una sorta di struttura ad albero in cima a una lista. Personalmente, sono un grande fan delle strutture di dati persistenti e gli alberi sembrano essere più facili da ragionare in quel particolare contesto. Non credo sia una coincidenza che Clojure, Scala, et al. sembrano convergere su una sorta di Hash in stile Bagwell come struttura di dati di base. (Phil Bagwell è stato anche coinvolto nella riprogettazione del quadro delle collezioni di Scala per Scala 2.8.) Gli elenchi di salti sono comunque belli.
Jörg W Mittag,

Questa è la migliore spiegazione di come funziona un elenco skip che io abbia mai letto.
gaborous
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.