Quando utilizzare un elenco collegato su un elenco di array / array?


176

Uso un sacco di elenchi e matrici ma devo ancora imbattermi in uno scenario in cui l'elenco di array non può essere usato altrettanto facilmente, se non più facile dell'elenco collegato. Speravo che qualcuno potesse darmi alcuni esempi di quando l'elenco collegato è notevolmente migliore.


In Java, ArrayList e LinkedList usano esattamente lo stesso codice diverso dal costruttore. Il tuo "elenco di array ... usato con la stessa facilità o facilità rispetto all'elenco collegato" non ha senso. Fornisci un esempio di ArrayList come "più semplice" di un LinkedList.
S.Lott

2
Controllare anche questo, stackoverflow.com/questions/322715/...
NoName


3
S.Lott Questo non è vero. Java ArrayList è un wrapper attorno a un array, con alcune funzioni di utilità aggiunte. Un elenco collegato è, ovviamente, un elenco collegato. developer.classpath.org/doc/java/util/ArrayList-source.html
kingfrito_5005

Risposte:


261

Le liste collegate sono preferibili rispetto alle matrici quando:

  1. hai bisogno di inserimenti / eliminazioni a tempo costante dall'elenco (come nel calcolo in tempo reale in cui la prevedibilità del tempo è assolutamente critica)

  2. non sai quanti elementi saranno presenti nell'elenco. Con gli array, potrebbe essere necessario ri-dichiarare e copiare la memoria se l'array diventa troppo grande

  3. non è necessario l'accesso casuale a nessun elemento

  4. vuoi essere in grado di inserire elementi in mezzo all'elenco (come una coda di priorità)

Le matrici sono preferibili quando:

  1. hai bisogno di un accesso indicizzato / casuale agli elementi

  2. si conosce il numero di elementi nell'array in anticipo in modo da poter allocare la quantità corretta di memoria per l'array

  3. hai bisogno di velocità durante l'iterazione di tutti gli elementi in sequenza. È possibile utilizzare la matematica del puntatore sull'array per accedere a ciascun elemento, mentre è necessario cercare il nodo in base al puntatore per ciascun elemento nell'elenco collegato, il che può causare errori di pagina che possono provocare hit delle prestazioni.

  4. la memoria è una preoccupazione. Gli array riempiti occupano meno memoria degli elenchi collegati. Ogni elemento nella matrice è solo i dati. Ciascun nodo dell'elenco collegato richiede i dati nonché uno (o più) puntatori agli altri elementi dell'elenco collegato.

Gli elenchi di array (come quelli in .Net) offrono i vantaggi degli array, ma allocano dinamicamente le risorse per te in modo da non doverti preoccupare troppo delle dimensioni dell'elenco e puoi eliminare elementi in qualsiasi indice senza alcuno sforzo o ri mescolando elementi intorno. Per quanto riguarda le prestazioni, gli arrailisti sono più lenti degli array grezzi.


7
Buon inizio, ma questo lascia fuori le cose importanti: gli elenchi supportano la condivisione della struttura, gli array sono più densi e hanno una migliore localizzazione.
Darius Bacon,

1
In pratica, la differenza di prestazioni tra array e array è trascurabile. Ciò presuppone che si faccia un confronto comparabile e, ad esempio, quando si conosce in anticipo la dimensione, si comunica all'arrailista.
svick,

40
Da quando LinkedList ha inserimenti / eliminazioni O (1) (che cosa suppongo intendi quando dici inserimenti / eliminazioni a tempo costante )? Inserire elementi nel mezzo di una LinkedList è sempre O (n)
Pacerier

28
Le liste collegate hanno inserimenti O (1) se ti trovi già nella posizione dell'inserimento (tramite un iteratore). Non sempre, però.
Adam,

4
L'uso di elenchi collegati per le code prioritarie è un'idea molto stupida. Gli heap dinamici supportati da array consentono l'inserimento ammortizzato O (lg n) e l'eliminazione-min logaritmica nel caso peggiore, e sono tra le strutture di coda prioritarie pratiche più veloci.
Fred Foo,

54

Le matrici hanno accesso casuale O (1), ma sono molto costose per aggiungere o rimuovere elementi da.

Gli elenchi collegati sono davvero economici per aggiungere o rimuovere elementi ovunque e per iterare, ma l'accesso casuale è O (n).


3
La rimozione di elementi dalla fine di un array è un tempo contante, così come l'inserimento / la rimozione di elementi da entrambe le estremità di un elenco collegato. Nel mezzo ... non tanto per nessuno dei due.
Joey,

1
@Joey non è l'inserimento / eliminazione alla fine di un Elenco collegato O (n)? A meno che tu non sia già posizionato sul penultimo link, avrai comunque bisogno di O (n) passaggi per trovare l'ultimo elemento, no?
Alex Moore-Niemi,

@ AlexMoore-Niemi: per un elenco collegato singolarmente, sì. Ma molti hanno collegamenti avanti e indietro e quindi mantengono i puntatori su entrambe le estremità.
Joey,

2
Avere una lista doppiamente collegata ti farà fare ricerche avanti e indietro, a meno che il tuo LL non abbia ordinato valori ... e lo scenario peggiore è ancora O (n)
securecurve

"Gli elenchi collegati sono davvero economici per aggiungere o rimuovere elementi ovunque e per iterare" non è del tutto vero. Se voglio rimuovere un elemento che si trova nel mezzo di un elenco collegato, dovrò iterare dall'inizio fino a raggiungere l'elemento nell'elenco. Tempo O (n / 2) in cui n = numero di elementi nell'elenco. Dalla tua risposta sembra che tu stia suggerendo il suo tempo costante O (1) come se fosse nella matrice. È tempo costante per aggiungere / rimuovere dal nodo head / root di un elenco collegato.
Yawar Murtaza,

22
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

Le liste di array sono utili per scrivere-una-sola-lettura-molte o appendici, ma male per aggiungere / rimuovere dalla parte anteriore o centrale.


14

Per aggiungere alle altre risposte, la maggior parte delle implementazioni dell'elenco di array riservano ulteriore capacità alla fine dell'elenco in modo che nuovi elementi possano essere aggiunti alla fine dell'elenco entro O (1). Quando viene superata la capacità di un elenco di array, un nuovo array più grande viene allocato internamente e tutti gli elementi precedenti vengono copiati. Di solito, il nuovo array ha una dimensione doppia rispetto a quello vecchio. Ciò significa che, in media , l'aggiunta di nuovi elementi alla fine di un elenco di array è un'operazione O (1) in queste implementazioni. Quindi, anche se non si conosce in anticipo il numero di elementi, un elenco di array potrebbe essere più veloce di un elenco collegato per l'aggiunta di elementi, purché si aggiungano alla fine. Ovviamente, l'inserimento di nuovi elementi in posizioni arbitrarie in un elenco di array è ancora un'operazione O (n).

L'accesso agli elementi in un elenco di array è anche più veloce di un elenco collegato, anche se gli accessi sono sequenziali. Questo perché gli elementi dell'array sono memorizzati in una memoria contigua e possono essere memorizzati facilmente nella cache. I nodi dell'elenco collegato possono potenzialmente essere sparsi su molte pagine diverse.

Consiglierei di utilizzare un elenco collegato solo se sai che stai per inserire o eliminare elementi in posizioni arbitrarie. Gli elenchi di array saranno più veloci praticamente per tutto il resto.


1
Inoltre, è anche possibile implementare elenchi collegati (nel senso del tipo di dati astratto) utilizzando array dinamici. In questo modo è possibile sfruttare la cache del computer pur avendo ammortizzato gli inserimenti e le eliminazioni a tempo costante in testa all'elenco e anche gli inserimenti e le eliminazioni a tempo costante nel mezzo dell'elenco quando si ha l'indice dell'elemento dopo il quale l'inserimento deve essere fatto o l'indice dell'elemento da eliminare (non sono necessari turni / spostamenti). Un buon riferimento per questo è CLRS 10.3 .
Domenico De Felice,

7

Il vantaggio degli elenchi appare se è necessario inserire elementi nel mezzo e non si desidera iniziare a ridimensionare l'array e spostare le cose.

Hai ragione in questo non è generalmente il caso. Ho avuto alcuni casi molto specifici come quello, ma non troppi.


Lo spostamento e il ridimensionamento dell'array è ciò che effettivamente accade quando si eseguono inversioni nel mezzo. Avrai solo bisogno di cambiare senza ridimensionare se non colpisci il limite di ammortamento.
securecurve,

3

Tutto dipende dal tipo di operazione che stai eseguendo durante l'iterazione, tutte le strutture di dati sono in bilico tra tempo e memoria e in base alle nostre esigenze dovremmo scegliere il DS giusto. Quindi ci sono alcuni casi in cui LinkedList è più veloce di array e viceversa. Considera le tre operazioni di base sulle strutture di dati.

  • ricerca

Poiché l'array è una struttura di dati basata sull'indice, la ricerca dell'array.get (indice) richiederà O (1) mentre l'elenco collegato non è indice DS, quindi dovrai attraversare fino all'indice, dove indice <= n, n è la dimensione dell'elenco collegato, quindi l'array è più veloce dell'elenco collegato quando ha accesso casuale agli elementi.

Q. Quindi qual è la bellezza dietro questo?

Poiché gli array sono blocchi di memoria contigui, grossi pezzi di questi verranno caricati nella cache al primo accesso, ciò rende relativamente rapido l'accesso agli elementi rimanenti dell'array, tanto quanto accediamo agli elementi nella località dell'array di riferimento aumenta anche in questo modo si riduce la cattura manca, la località cache si riferisce alle operazioni che si trovano nella cache e quindi eseguono molto più velocemente rispetto alla memoria, fondamentalmente nell'array massimizziamo le possibilità di accesso sequenziale agli elementi nella cache. Mentre gli elenchi collegati non sono necessariamente in blocchi di memoria contigui, non vi è alcuna garanzia che gli elementi che appaiono in sequenza nell'elenco siano effettivamente disposti uno vicino all'altro in memoria, ciò significa un minor numero di accessi alla cache, ad es.

  • Inserimento

Questo è facile e veloce in LinkedList poiché l'inserimento è un'operazione O (1) in LinkedList (in Java) rispetto all'array, considera il caso quando l'array è pieno, dobbiamo copiare il contenuto nel nuovo array se l'array è pieno che rende l'inserimento di un elemento in ArrayList di O (n) nel peggiore dei casi, mentre ArrayList deve anche aggiornare il suo indice se si inserisce qualcosa ovunque tranne alla fine dell'array, in caso di elenco collegato non è necessario ridimensionarlo, è sufficiente aggiorna i puntatori.

  • cancellazione

Funziona come inserimenti e meglio in LinkedList di array.


2

Quelle sono le implementazioni usate più comuni di Collection.

Lista di array:

  • inserire / cancellare alla fine generalmente O (1) nel peggiore dei casi O (n)

  • inserisci / elimina nel mezzo O (n)

  • recuperare qualsiasi posizione O (1)

Lista collegata:

  • inserisci / elimina in qualsiasi posizione O (1) (nota se hai un riferimento all'elemento)

  • recuperare nel mezzo O (n)

  • recuperare il primo o l'ultimo elemento O (1)

Vettore: non usarlo. È una vecchia implementazione simile a ArrayList ma con tutti i metodi sincronizzati. Non è l'approccio corretto per un elenco condiviso in un ambiente multithreading.

HashMap

inserire / cancellare / recuperare con la chiave in O (1)

TreeSet inserisci / cancella / contiene in O (registro N)

HashSet insert / remove / contiene / size in O (1)


1

In realtà la localizzazione della memoria ha un'enorme influenza sulle prestazioni nell'elaborazione reale.

Il maggiore utilizzo dello streaming su disco nell'elaborazione dei "big data" rispetto all'accesso casuale mostra come strutturare la vostra applicazione attorno a questo possa migliorare notevolmente le prestazioni su una scala più ampia.

Se esiste un modo per accedere ad un array in sequenza, questo è di gran lunga il migliore. Progettare con questo come obiettivo dovrebbe essere almeno considerato se le prestazioni sono importanti.


0

Hmm, Arraylist può essere usato in casi come i seguenti immagino:

  1. non sei sicuro di quanti elementi saranno presenti
  2. ma devi accedere a tutti gli elementi in modo casuale tramite l'indicizzazione

Ad esempio, è necessario importare e accedere a tutti gli elementi in un elenco di contatti (la cui dimensione non è nota)


0

Utilizzare l'elenco collegato per l'ordinamento Radix su array e per operazioni polinomiali.


0

1) Come spiegato sopra, le operazioni di inserimento e rimozione forniscono buone prestazioni (O (1)) in LinkedList rispetto ad ArrayList (O (n)). Quindi, se vi è un requisito di frequente aggiunta e cancellazione nell'applicazione, allora LinkedList è la scelta migliore.

2) Le operazioni di ricerca (ottieni metodo) sono veloci in Arraylist (O (1)) ma non in LinkedList (O (n)), quindi Se ci sono meno operazioni di aggiunta e rimozione e più requisiti per le operazioni di ricerca, ArrayList sarebbe la soluzione migliore.


0

Penso che la differenza principale sia se hai spesso bisogno di inserire o rimuovere elementi dalla cima dell'elenco.

Con un array, se si rimuove qualcosa dalla cima dell'elenco, la complessità è o (n) perché tutti gli indici degli elementi dell'array dovranno spostarsi.

Con un elenco collegato, è o (1) perché è necessario solo creare il nodo, riassegnare la testa e assegnare il riferimento a next come testa precedente.

Quando si inseriscono o rimuovono frequentemente alla fine dell'elenco, le matrici sono preferibili perché la complessità sarà o (1), non è necessario reindicizzare, ma per un elenco collegato sarà o (n) perché è necessario andare dalla testa all'ultimo nodo.

Penso che la ricerca sia nella lista collegata sia negli array sarà o (log n) perché probabilmente userete una ricerca binaria.


0

Ho fatto alcuni benchmarking e ho scoperto che la classe list è in realtà più veloce di LinkedList per l'inserimento casuale:

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 20000;
            Random rand = new Random(12345);

            Stopwatch watch = Stopwatch.StartNew();
            LinkedList<int> ll = new LinkedList<int>();
            ll.AddLast(0);
            for (int i = 1; i < count; i++)
            {
                ll.AddBefore(ll.Find(rand.Next(i)),i);

            }
            Console.WriteLine("LinkedList/Random Add: {0}ms", watch.ElapsedMilliseconds);

            watch = Stopwatch.StartNew();
            List<int> list = new List<int>();
            list.Add(0);
            for (int i = 1; i < count; i++)
            {
                list.Insert(list.IndexOf(rand.Next(i)), i);

            }
            Console.WriteLine("List/Random Add: {0}ms", watch.ElapsedMilliseconds);

            Console.ReadLine();
        }
    }
}

Sono necessari 900 ms per l'elenco collegato e 100 ms per la classe elenco.

Crea elenchi di numeri interi successivi. Ogni nuovo numero intero viene inserito dopo un numero casuale che è già nell'elenco. Forse la classe List utilizza qualcosa di meglio di un semplice array.


L'elenco è un'interfaccia, non una classe
borgmater

0

Le matrici, di gran lunga, sono le strutture di dati più utilizzate. Tuttavia, gli elenchi collegati si rivelano utili nel loro modo unico in cui gli array sono goffi - o costosi, per non dire altro.

Gli elenchi collegati sono utili per implementare stack e code in situazioni in cui la loro dimensione è soggetta a variazioni. Ogni nodo nell'elenco collegato può essere inserito o estratto senza disturbare la maggior parte dei nodi. Lo stesso vale per l'inserimento / eliminazione di nodi da qualche parte nel mezzo. Negli array, tuttavia, tutti gli elementi devono essere spostati, il che è un lavoro costoso in termini di tempo di esecuzione.

Alberi binari e alberi di ricerca binari, tabelle hash e prove sono alcune delle strutture di dati in cui - almeno in C - sono necessari elenchi collegati come ingrediente fondamentale per costruirli.

Tuttavia, gli elenchi collegati dovrebbero essere evitati in situazioni in cui si prevede che sia in grado di chiamare qualsiasi elemento arbitrario dal suo indice.


0

Una semplice risposta alla domanda può essere data usando questi punti:

  1. Le matrici devono essere utilizzate quando è richiesta una raccolta di elementi di dati di tipo simile. Considerando che l'elenco collegato è una raccolta di elementi collegati dati di tipo misto noti come nodi.

  2. Nell'array, si può visitare qualsiasi elemento nel tempo O (1). Considerando che, nella lista collegata avremmo bisogno di attraversare l'intera lista collegata dalla testa al nodo richiesto impiegando O (n) tempo.

  3. Per le matrici, una dimensione specifica deve essere dichiarata inizialmente. Ma le liste collegate hanno dimensioni dinamiche.

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.