web-dev-qa-db-fra.com

Tableau versus liste chaînée

Pourquoi quelqu'un voudrait-il utiliser une liste chaînée sur un tableau?

Coder une liste chaînée est sans doute un peu plus de travail que d’utiliser un tableau et on peut se demander ce qui justifierait cet effort supplémentaire.

Je pense que l'insertion de nouveaux éléments est triviale dans une liste chaînée, mais c'est une tâche majeure dans un tableau. Existe-t-il d'autres avantages à utiliser une liste chaînée pour stocker un ensemble de données par rapport à son stockage dans un tableau?

Cette question n'est pas une copie de cette question car l'autre question concerne spécifiquement une classe Java particulière, alors que cette question concerne les structures de données générales.

190
Onorio Catenacci
  • Il est plus facile de stocker des données de différentes tailles dans une liste chaînée. Un tableau suppose que chaque élément a exactement la même taille.
  • Comme vous l'avez mentionné, il est plus facile pour une liste chaînée de se développer de manière organique. La taille d'un tableau doit être connue à l'avance ou recréée si elle doit s'agrandir.
  • Pour mélanger une liste chaînée, il suffit de changer ce qui pointe sur quoi. Mélanger un tableau est plus compliqué et/ou prend plus de mémoire.
  • Tant que vos itérations se produisent toutes dans un contexte "foreach", vous ne perdez aucune performance en itération.
145
Ryan

Une autre bonne raison est que les listes chaînées se prêtent bien à des implémentations efficaces multithreads. La raison en est que les modifications ont tendance à être locales - n'affectant qu'un ou deux pointeurs pour insertion et suppression au niveau d'une partie localisée de la structure de données. Ainsi, vous pouvez avoir plusieurs threads travaillant sur la même liste chaînée. De plus, il est possible de créer des versions sans verrouillage à l'aide d'opérations de type CAS et d'éviter les verrous lourds.

Avec une liste chaînée, les itérateurs peuvent également parcourir la liste pendant les modifications. Dans le cas optimiste où les modifications n'entrent pas en collision, les itérateurs peuvent continuer sans conflit.

Avec un tableau, toute modification qui en modifie la taille nécessitera probablement le verrouillage d'une grande partie du tableau. En fait, il est rare que cela se fasse sans verrouillage global sur l'ensemble du tableau, de sorte que les modifications arrêtent les affaires du monde.

171
Alex Miller

Wikipedia a une très bonne section sur les différences.

Les listes chaînées présentent plusieurs avantages par rapport aux tableaux. Les éléments peuvent être insérés indéfiniment dans les listes chaînées, tandis qu'un tableau sera soit complet soit utilisé, soit redimensionné, opération coûteuse qui risque même de ne pas être possible si la mémoire est fragmentée. De la même manière, une matrice dont de nombreux éléments sont supprimés peut devenir vidée de manière inutile ou doit être réduite.

D'autre part, les tableaux permettent un accès aléatoire, tandis que les listes chaînées ne permettent qu'un accès séquentiel aux éléments. En fait, les listes à lien unique ne peuvent être parcourues que dans un seul sens. Cela rend les listes chaînées inutilisables pour les applications où il est utile de rechercher rapidement un élément par son index, tel que heapsort. L'accès séquentiel sur les baies est également plus rapide que sur les listes chaînées sur de nombreuses machines en raison de la localisation des caches de référence et de données. Les listes chaînées ne bénéficient presque pas du cache.

Un autre inconvénient des listes chaînées est le stockage supplémentaire nécessaire aux références, ce qui les rend souvent peu pratiques pour les listes de petits éléments de données tels que des caractères ou des valeurs booléennes. Il peut également être lent et, avec un allocateur naïf et inutile, d'allouer de la mémoire séparément pour chaque nouvel élément, problème généralement résolu à l'aide de pools de mémoire.

http://en.wikipedia.org/wiki/Linked_list

123
Jonas Klemming

J'ajouterai un autre - les listes peuvent agir comme purement fonctionnelles structures de données.

Par exemple, vous pouvez avoir des listes complètement différentes partageant la même section d'extrémité

a = (1 2 3 4, ....)
b = (4 3 2 1 1 2 3 4 ...)
c = (3 4 ...)

c'est à dire.:

b = 4 -> 3 -> 2 -> 1 -> a
c = a.next.next  

sans avoir à copier les données désignées par a dans b et c.

C'est pourquoi ils sont si populaires dans les langages fonctionnels, qui utilisent des variables immuables - les opérations prepend et tail peuvent se dérouler librement sans avoir à copier les données d'origine. Ces fonctions sont très importantes lorsque vous traitez des données comme immuables. .

55
Brian

La fusion de deux listes liées (en particulier de deux listes doublement liées) est beaucoup plus rapide que la fusion de deux tableaux (en supposant que la fusion soit destructive). Le premier prend O (1), le dernier prend O (n).

EDIT: Pour clarifier, je voulais dire "fusionner" ici dans le sens non ordonné, pas comme dans un type de fusion. Peut-être que "concaténer" aurait été une meilleure Parole.

28
rampion

En plus de faciliter l’insertion au milieu de la liste, il est également beaucoup plus facile de la supprimer du milieu d’une liste chaînée qu’un tableau.

Mais franchement, je n'ai jamais utilisé de liste chaînée. Chaque fois que j'avais besoin d'insertion et de suppression rapides, j'avais également besoin d'une recherche rapide. Je suis donc allé à un hachage ou à un dictionnaire.

28
Tom Ritter

Tout d’abord, en C++, les listes chaînées ne devraient pas poser plus de problèmes que de travailler avec un tableau. Vous pouvez utiliser le std :: list ou le liste de pointeur boost pour les listes chaînées. Les principaux problèmes liés aux listes chaînées et aux tableaux sont l’espace supplémentaire requis pour les pointeurs et un terrible accès aléatoire. Vous devriez utiliser une liste chaînée si vous

  • vous n'avez pas besoin d'un accès aléatoire aux données
  • vous allez ajouter/supprimer des éléments, en particulier au milieu de la liste
17
David Nehme

Un argument largement méconnu pour ArrayList et contre LinkedList est le suivant: les LinkedLists sont inconfortables lors du débogage. Temps passé par les développeurs de maintenance à comprendre le programme, par exemple pour trouver des bogues, augmente et IMHO ne justifie parfois pas les nanosecondes dans les améliorations de performances ou octets dans la consommation de mémoire dans les applications d'entreprise. Parfois (enfin, cela dépend bien sûr du type d’application), il vaut mieux perdre quelques octets, mais avoir une application plus facile à gérer ou à comprendre.

Par exemple, dans un environnement Java et à l'aide du débogueur Eclipse, le débogage d'un ArrayList révélera une structure très facile à comprendre:

arrayList   ArrayList<String>
  elementData   Object[]
    [0] Object  "Foo"
    [1] Object  "Foo"
    [2] Object  "Foo"
    [3] Object  "Foo"
    [4] Object  "Foo"
    ...

D'autre part, regarder le contenu d'une LinkedList et rechercher des objets spécifiques devient un cauchemar pour Expand-The-Tree, sans parler de la surcharge cognitive nécessaire pour filtrer les éléments internes de LinkedList:

linkedList  LinkedList<String>
    header  LinkedList$Entry<E>
        element E
        next    LinkedList$Entry<E>
            element E   "Foo"
            next    LinkedList$Entry<E>
                element E   "Foo"
                next    LinkedList$Entry<E>
                    element E   "Foo"
                    next    LinkedList$Entry<E>
                    previous    LinkedList$Entry<E>
                    ...
                previous    LinkedList$Entry<E>
            previous    LinkedList$Entry<E>
        previous    LinkedList$Entry<E>
17
mhaller

Pour moi c'est comme ça,

  1. Accès

    • Listes liées n'autorise qu'un accès séquentiel aux éléments. Ainsi, les complexités algorithmiques sont d'ordre de O (n)
    • Arrays autorise un accès aléatoire à ses éléments et la complexité est donc de l'ordre de O (1)
  2. Espace de rangement

    • Les listes chaînées nécessitent un stockage supplémentaire pour les références. Cela les rend impraticables pour les listes de petits éléments de données tels que les caractères ou les valeurs booléennes.
    • Les tableaux n'ont pas besoin d'un espace de stockage supplémentaire pour pointer vers le prochain élément de données. Chaque élément est accessible via des index.
  3. Taille

    • La taille de Listes liées est dynamique par nature.
    • La taille de array est limitée à la déclaration.
  4. Insertion/Suppression

    • Les éléments peuvent être insérés et supprimés dans listes chaînées indéfiniment.
    • L'insertion/suppression de valeurs dans tableaux sont très coûteuses. Cela nécessite une réallocation de la mémoire.
14
Sulman

Deux choses:

Coder une liste chaînée est sans doute un peu plus de travail que d’utiliser un tableau et il se demandait ce qui justifierait cet effort supplémentaire.

Ne codez jamais une liste chaînée lorsque vous utilisez C++. Il suffit d'utiliser la STL. La difficulté de sa mise en œuvre ne devrait jamais être une raison pour choisir une structure de données plutôt qu'une autre, car la plupart sont déjà implémentées.

En ce qui concerne les différences réelles entre un tableau et une liste chaînée, le plus important pour moi est la manière dont vous envisagez d'utiliser la structure. J'utiliserai le terme vecteur car c'est le terme pour un tableau redimensionnable en C++.

L'indexation dans une liste chaînée est lente car vous devez parcourir la liste pour accéder à l'index donné, alors qu'un vecteur est contigu en mémoire et que vous pouvez y arriver à l'aide du pointeur mathématique.

Il est facile d’ajouter à la fin ou au début d’une liste chaînée, puisqu’il ne faut mettre à jour qu’un seul lien; dans un vecteur, vous devrez peut-être redimensionner et copier le contenu.

Supprimer un élément d'une liste est facile, puisqu'il vous suffit de casser une paire de liens, puis de les attacher ensemble. Le retrait d'un élément d'un vecteur peut être plus rapide ou plus lent, selon que vous vous souciez de l'ordre. Le remplacement du dernier élément par-dessus celui que vous souhaitez supprimer est plus rapide, tandis que tout décaler par la suite est plus lent, mais conserve l'ordre.

11
bradtgmurray

Eric Lippert a récemment eu un post sur l'une des raisons pour lesquelles les tableaux doivent être utilisés de manière conservatrice.

10
Rik

L'insertion et la suppression rapides sont en effet les meilleurs arguments pour les listes chaînées. Si votre structure se développe de manière dynamique et qu'aucun accès à un élément n'est nécessaire (par exemple, des piles et des files d'attente dynamiques), les listes chaînées constituent un bon choix.

8
Firas Assaad

Outre l'ajout et la suppression au milieu de la liste, j'aime davantage les listes chaînées, car elles peuvent s'agrandir et se réduire de manière dynamique.

7
Vasil

La liste chaînée est particulièrement utile lorsque la collection grossit et diminue constamment. Par exemple, il est difficile d'imaginer essayer d'implémenter une file d'attente (ajouter à la fin, supprimer du premier plan) à l'aide d'un tableau: vous perdriez tout votre temps à faire évoluer les choses. Par contre, c'est trivial avec une liste chaînée.

7
James Curran

Personne ne code plus sa propre liste chaînée. Ce serait idiot. La prémisse que l'utilisation d'une liste liée prend plus de code est tout simplement fausse.

De nos jours, la construction d'une liste chaînée n'est qu'un exercice pour les étudiants afin qu'ils puissent comprendre le concept. Au lieu de cela, tout le monde utilise une liste prédéfinie. En C++, d'après la description de notre question, cela signifierait probablement un vecteur stl (#include <vector>).

Par conséquent, choisir une liste chaînée par rapport à un tableau revient entièrement à peser les différentes caractéristiques de chaque structure par rapport aux besoins de votre application. Surmonter le fardeau de programmation supplémentaire ne devrait avoir aucun impact sur la décision.

7
Joel Coehoorn

En voici une rapide: l'enlèvement des objets est plus rapide.

7
itsmatt

C’est vraiment une question d’efficacité, la surcharge d’insérer, de supprimer ou de déplacer (lorsque vous n’échangez pas simplement) des éléments d’une liste chaînée est minime, c’est-à-dire que l’opération elle-même est O (1), versets O(n) pour un tableau. Cela peut faire une différence significative si vous travaillez beaucoup sur une liste de données. Vous avez choisi vos types de données en fonction de la manière dont vous allez les exploiter et vous avez choisi le plus efficace pour l'algorithme que vous utilisez.

6
Steve Baker

Les tableaux ont un sens lorsque le nombre exact d’éléments sont connus et que la recherche par index est utile. Par exemple, si je voulais stocker l'état exact de ma sortie vidéo à un moment donné sans compression, j'utiliserais probablement un tableau de taille [1024] [768]. Cela me fournira exactement ce dont j'ai besoin, et une liste serait beaucoup, beaucoup plus lente à obtenir la valeur d'un pixel donné. Dans les endroits où un tableau n'a pas de sens, il existe généralement de meilleurs types de données qu'une liste pour traiter efficacement les données.

6
tloach

Liste de tableaux vs vs:

  1. L'allocation de mémoire de tableau échouera parfois à cause d'une mémoire fragmentée.
  2. La mise en cache est meilleure dans les tableaux car tous les éléments se voient allouer un espace mémoire contigu.
  3. Le codage est plus complexe que les tableaux.
  4. Aucune contrainte de taille sur la liste chaînée, contrairement aux tableaux
  5. L'insertion/suppression est plus rapide dans la liste chaînée et l'accès est plus rapide dans les tableaux.
  6. Liés Liste mieux du point de vue multi-threading.
6
AKS

La liste liée est plus une surcharge à maintenir qu'une matrice, elle nécessite également un stockage de mémoire supplémentaire. Tous ces points sont approuvés. Mais il y a quelques choses que les tableaux ne peuvent pas faire. Dans de nombreux cas, supposons que vous souhaitiez un tableau de longueur 10 ^ 9 que vous ne pouvez pas obtenir car il est nécessaire d’obtenir un emplacement de mémoire en continu. La liste chaînée pourrait être un sauveur ici.

Supposons que vous souhaitiez stocker plusieurs éléments avec des données, ils peuvent ensuite être facilement étendus dans la liste liée.

Les conteneurs STL ont généralement une implémentation de liste chaînée derrière la scène.

3
iec2011007

Dans un tableau, vous avez le privilège d'accéder à n'importe quel élément de la durée O(1). Ainsi, il convient pour des opérations telles que la recherche binaire, le tri rapide, etc. La liste chaînée, quant à elle, convient à la suppression d'insertion, comme c'est le cas en O(1). Les deux présentent des avantages et des inconvénients, et privilégier l'un au détriment de ce que l'on souhaite mettre en œuvre.

- La plus grande question est peut-on avoir un hybride des deux. Quelque chose comme ce que python et Perl implémentent en tant que listes.

3
pranjal

Supposons que vous ayez un ensemble ordonné que vous souhaitez également modifier en ajoutant et en supprimant des éléments. En outre, vous devez avoir la possibilité de conserver une référence à un élément de manière à pouvoir obtenir ultérieurement un élément précédent ou suivant. Par exemple, une liste de tâches ou un ensemble de paragraphes dans un livre.

Tout d'abord, il convient de noter que si vous souhaitez conserver des références à des objets en dehors de l'ensemble lui-même, vous finirez probablement par stocker des pointeurs dans le tableau plutôt que par les objets eux-mêmes. Sinon, vous ne pourrez pas insérer dans un tableau. Si des objets sont incorporés dans le tableau, ils seront déplacés lors des insertions et leurs pointeurs deviendront invalides. Même chose pour les index de tableau.

Comme vous l'avez constaté vous-même, votre premier problème est que la liste chaînée par insertion permet de l'insérer dans O (1), mais un tableau aurait généralement besoin de O (n). Ce problème peut être partiellement résolu - il est possible de créer une structure de données qui donne une interface d'accès ordinal semblable à un tableau où la lecture et l'écriture sont, au pire, logarithmiques.

Votre deuxième problème, et le plus grave, est que, étant donné qu’un élément trouvant le prochain élément est O (n). Si le jeu n'a pas été modifié, vous pouvez conserver l'index de l'élément comme référence au lieu du pointeur, faisant ainsi de find-next une opération O(1), mais comme c'est tout ce que vous avez, c'est un pointeur sur l'objet lui-même et aucun moyen de déterminer son index actuel dans le tableau autrement qu'en analysant l'ensemble du "tableau". Il s'agit d'un problème insurmontable pour les tableaux. Même si vous pouvez optimiser les insertions, vous ne pouvez rien faire pour optimiser l'opération de type find-next.

3
DenNukem

Liste liée

C'est plus préférable quand il s'agit d'insertion! Fondamentalement, ce qui est fait est qu'il traite avec le pointeur

1 -> 3 -> 4

Insert (2)

1 ........ 3 ...... 4
..... 2

Finalement

1 -> 2 -> 3 -> 4

Une flèche des 2 points à 3 et la flèche de 1 points à 2

Facile!

Mais de tableau

| 1 | 3 | 4 |

Insérer (2) | 1 | 3 | | 4 | | 1 | | 3 | 4 | | 1 | 2 | 3 | 4 |

Tout le monde peut visualiser la différence! Juste pour 4 index nous effectuons 3 étapes

Et si la longueur du tableau est d'un million alors? Le tableau est-il efficace? La réponse est non! :)

La même chose vaut pour la suppression! Dans la liste liée, nous pouvons simplement utiliser le pointeur et annuler l'élément et ensuite dans la classe d'objet! Mais pour un tableau, nous devons effectuer shiftLeft ()

J'espère que ça t'as aidé! :)

3
Farah Nazifa

comme les tableaux sont de nature statique, toutes les opérations telles que l’allocation de mémoire ne se produisent que lors de la compilation. Le processeur doit donc faire moins d’efforts lors de son exécution.

3
debayan

La seule raison d'utiliser la liste chaînée est que l'insertion de l'élément est facile (supprimer également).

Un inconvénient pourrait être que les pointeurs prennent beaucoup de place.

Et à propos de cette codification est plus difficile: Généralement, vous n'avez pas besoin d'une liste de codes liés (ou seulement une fois), ils sont inclus dans STL et ce n'est pas si compliqué s'il vous reste à le faire.

2
user15453

Pourquoi quelqu'un voudrait-il utiliser une liste chaînée sur un tableau?

Ce n'est qu'une des raisons - si vous avez besoin d'une structure de données de liste chaînée et que le langage de programmation que vous utilisez ne prend pas en charge les pointeurs.

1
Ans

je pense aussi que cette liste de liens est plus performante que les tableaux. parce que nous traversons la liste des liens mais pas les tableaux

1
iram Arshad

Outre la commodité des insertions et des suppressions, la représentation en mémoire de la liste chaînée est différente de celle des tableaux. Il n'y a aucune restriction sur le nombre d'éléments dans une liste liée, alors que dans les tableaux, vous devez spécifier le nombre total d'éléments. Vérifiez this article.

1
Sajjad Abdullah

Pourquoi une liste chaînée sur un tableau? Comme certains l’ont déjà dit, les insertions et les suppressions sont plus rapides.

Mais peut-être que nous n'avons pas à vivre avec les limites de l'un ou de l'autre et à tirer le meilleur parti des deux, en même temps ... hein?

Pour les suppressions de tableau, vous pouvez utiliser un octet 'Supprimé', pour représenter le fait qu'une ligne a été supprimée. Ainsi, la réorganisation du tableau n'est plus nécessaire. Pour alléger le fardeau des insertions ou de l’évolution rapide des données, utilisez une liste chaînée pour cela. Ensuite, lorsque vous vous référez à eux, demandez à votre logique de commencer par rechercher l’un puis l’autre. Ainsi, leur combinaison vous donne le meilleur des deux.

Si vous avez un très grand tableau, vous pouvez le combiner avec un autre tableau beaucoup plus petit ou une liste chaînée où le plus petit contient les 20, 50, 100 derniers éléments utilisés. Si celui qui est nécessaire ne figure pas dans la liste ou le tableau lié plus court, vous accédez au grand tableau. Si vous y trouvez, vous pouvez ensuite l'ajouter à la liste/au tableau lié plus petit en partant du principe que "les éléments les plus récemment utilisés sont le plus susceptibles d'être réutilisés" (et oui, éventuellement, en éliminant l'élément le moins récemment utilisé de la liste). Ce qui est vrai dans de nombreux cas et a résolu un problème que je devais résoudre dans un module de vérification des autorisations de sécurité .ASP, avec facilité, élégance et rapidité impressionnante.

1
Juan-Carlos

Alors que beaucoup d’entre vous ont évoqué les avantages majeurs de la liste chaînée par rapport à un tableau, la plupart des comparaisons montrent que l’un est meilleur/pire que l’autre.Eg. vous pouvez faire un accès aléatoire dans un tableau, mais pas possible dans une liste chaînée et autres. Cependant, cela suppose que les listes de liens et les tableaux vont être appliqués dans une application similaire. Cependant, une réponse correcte devrait être de savoir comment la liste de liens serait préférée au tableau et inversement dans le déploiement d'une application particulière. Supposons que vous souhaitiez implémenter une application de dictionnaire, que utiliseriez-vous? Tableau: mmm cela permettrait une récupération facile grâce à la recherche binaire et à d’autres algorithmes de recherche .. mais pensons que la liste de liens peut être meilleure ... Dites que vous voulez rechercher "Blob" dans le dictionnaire. Serait-il judicieux d'avoir une liste de liens de A-> B-> C-> D ----> Z et ensuite chaque élément de la liste pointant également vers un tableau ou une autre liste de tous les mots commençant par cette lettre ..

A -> B -> C -> ...Z
|    |    |
|    |    [Cat, Cave]
|    [Banana, Blob]
[Adam, Apple]

Maintenant, l'approche ci-dessus est-elle meilleure ou un tableau plat de [Adam, Pomme, Banane, Blob, Chat, Caverne]? Serait-ce même possible avec array? Un des principaux avantages de la liste de liens est donc qu’un élément peut non seulement pointer sur l’élément suivant, mais aussi sur une autre liste de liens/array/heap/ou n’importe quel autre emplacement mémoire. Un tableau est une mémoire contiguë plate découpée en blocs de la taille de l’élément qu’il va stocker. autre comme tu veux. De même, disons que vous créez un lecteur USB. Voulez-vous maintenant que les fichiers soient sauvegardés sous forme de tableau ou de liste de liens? Je pense que vous avez une idée de ce que je veux dire :)

1
Vikalp Veer

Selon votre langue, certains de ces inconvénients et avantages pourraient être pris en compte:

Langage de programmation C: Lorsque vous utilisez une liste chaînée (généralement à l'aide de pointeurs de structure), vous devez vous assurer que vous ne perdez pas de mémoire. Comme cela a été mentionné précédemment, les listes chaînées sont faciles à mélanger, car tout ce que nous faisions change de pointeur, mais allons-nous nous rappeler de tout libérer?

Java: Java possède une collecte automatique des déchets. Par conséquent, les fuites de mémoire ne seront pas un problème, mais les détails de la mise en œuvre de la liste chaînée sont cachés au programmeur de haut niveau. Des méthodes telles que la suppression d'un nœud du milieu de la liste constituent une procédure plus compliquée que certains utilisateurs du langage ne l'auraient imaginé.

1
SetSlapShot

La différence entre un tableau et une liste liée est qu'un tableau est une structure de données basée sur un index, chaque élément est associé à un index, tandis que la liste liée est une structure de données utilisant des références, chaque nœud est référé à un autre nœud. La taille du tableau est fixe alors que celle de la liste de liens n'est pas fixe.

0
sagar

Les personnes utilisant la liste de liens doivent lire. Les gens vont tomber amoureux de tableau à nouveau. Il parle d'exeuction hors d'ordre, de prélecture de matériel, de latence de la mémoire, etc.

http://www.futurechips.org/whilets-for-researchers/quick-post-linked-lists.html

0
Ashkrit Sharma