Pourquoi quelqu'un voudrait-il utiliser une liste chaînée sur un tableau?
Coder une liste chaînée est, sans aucun doute, un peu plus de travail que d'utiliser un tableau et on peut se demander ce qui justifierait l'effort supplémentaire.
Je pense que l'insertion de nouveaux éléments est triviale dans une liste chaînée, mais c'est une corvée majeure dans un tableau. Y a-t-il d'autres avantages à utiliser une liste chaînée pour stocker un ensemble de données par rapport au stockage dans un tableau?
Cette question n'est pas un double 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.
arrays
data-structures
linked-list
language-agnostic
Onorio Catenacci
la source
la source
Réponses:
la source
Une autre bonne raison est que les listes chaînées se prêtent bien à des implémentations multi-thread efficaces. La raison en est que les changements ont tendance à être locaux - n'affectant qu'un ou deux pointeurs à insérer et à supprimer sur 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 en utilisant des opérations de type CAS et d'éviter complètement 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 ne se heurtent pas, les itérateurs peuvent continuer sans contention.
Avec un tableau, tout changement qui modifie la taille du tableau nécessitera probablement le verrouillage d'une grande partie du tableau et en fait, il est rare que cela se fasse sans verrou global sur l'ensemble du tableau, de sorte que les modifications arrêtent les affaires du monde.
la source
ConcurrentSkipListMap
ouConcurrentSkipListMap
listes ne sont pas, même si « Liste » se produit quelque part dans leur nom. Les deux nécessitent des clés qui seront triées et uniques. Si vous avez besoin d'uneList
structure de données, c'est-à-dire qui permet de dupliquer des éléments dans un ordre arbitraire, cela ne convient pas et vous devrez faire de grands efforts pour créer une structure de données commeLinkedList
une chose pouvant être mise à jour simultanément. Si vous n'avez besoin que d'une file d'attente ou d'une suppression simultanée, eh bien oui, il existe même des exemples existants, mais un concurrentList
… Je ne suis pas convaincu que cela soit même possible.Wikipedia a une très bonne section sur les différences.
http://en.wikipedia.org/wiki/Linked_list
la source
J'en ajouterai un autre - les listes peuvent agir comme des structures de données purement fonctionnelles .
Par exemple, vous pouvez avoir des listes complètement différentes partageant la même section de fin
c'est à dire:
sans avoir à copier les données pointées par
a
dansb
etc
.C'est pourquoi ils sont si populaires dans les langages fonctionnels, qui utilisent des variables immuables -
prepend
et lestail
opérations peuvent se produire librement sans avoir à copier les données d'origine - des fonctionnalités très importantes lorsque vous traitez les données comme immuables.la source
Outre l'insertion au milieu de la liste étant plus facile - il est également beaucoup plus facile de supprimer du milieu d'une liste liée qu'un tableau.
Mais franchement, je n'ai jamais utilisé de liste chaînée. Chaque fois que j'avais besoin d'une insertion et d'une suppression rapides, j'avais également besoin d'une recherche rapide, alors je suis allé à un HashSet ou à un dictionnaire.
la source
La fusion de deux listes liées (en particulier deux listes doublement liées) est beaucoup plus rapide que la fusion de deux tableaux (en supposant que la fusion est destructrice). Le premier prend O (1), le second prend O (n).
EDIT: Pour clarifier, je voulais dire «fusionner» ici dans le sens non ordonné, pas comme dans le type de fusion. Peut-être que «concaténer» aurait été un meilleur mot.
la source
Un argument largement méconnu pour ArrayList et contre LinkedList est que les LinkedLists sont inconfortables lors du débogage . Le temps passé par les développeurs de maintenance pour comprendre le programme, par exemple pour trouver des bogues, augmente et à mon humble avis ne justifie parfois pas les nanosecondes d'amélioration des performances ou les octets de consommation de mémoire dans les applicatons d'entreprise. Parfois (eh bien, cela dépend du type d'applications), il vaut mieux gaspiller quelques octets mais avoir une application plus facile à gérer ou à comprendre.
Par exemple, dans un environnement Java et en utilisant le débogueur Eclipse, le débogage d'une liste de tableaux révélera une structure très facile à comprendre:
D'un autre côté, regarder le contenu d'une LinkedList et trouver des objets spécifiques devient un cauchemar en cliquant sur Expand-The-Tree, sans parler de la surcharge cognitive nécessaire pour filtrer les éléments internes de LinkedList:
la source
Tout d'abord, en C ++, les listes liées ne devraient pas être beaucoup plus difficiles à utiliser qu'un tableau. Vous pouvez utiliser la liste std :: list ou la liste de pointeurs boost pour les listes liées. Les principaux problèmes avec les listes liées par rapport aux tableaux sont l'espace supplémentaire requis pour les pointeurs et un accès aléatoire terrible. Vous devez utiliser une liste chaînée si vous
la source
Pour moi, c'est comme ça,
Accès
Espace de rangement
Taille
Insertion / suppression
la source
Deux choses:
Ne codez jamais une liste liée lorsque vous utilisez C ++. Utilisez simplement la STL. La difficulté de l'implémentation ne devrait jamais être une raison de 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 liée, la grande chose pour moi est de savoir comment vous prévoyez d'utiliser la structure. Je vais utiliser 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é, tandis qu'un vecteur est contigu en mémoire et vous pouvez y accéder en utilisant le calcul du pointeur.
L'ajout à la fin ou au début d'une liste chaînée est facile, car vous n'avez qu'à mettre à jour un lien, où dans un vecteur vous devrez peut-être redimensionner et copier le contenu.
La suppression d'un élément d'une liste est facile, car il vous suffit de rompre une paire de liens, puis de les rattacher. La suppression d'un article d'un vecteur peut être plus rapide ou plus lente, selon que vous vous souciez de la commande. L'échange du dernier élément par-dessus l'élément que vous souhaitez supprimer est plus rapide, tandis que tout déplacer après est plus lent mais conserve l'ordre.
la source
Eric Lippert a récemment publié un article sur l'une des raisons pour lesquelles les tableaux doivent être utilisés avec prudence.
la source
L'insertion et la suppression rapides sont en effet les meilleurs arguments pour des listes chaînées. Si votre structure se développe de manière dynamique et qu'aucun accès à temps constant à aucun élément n'est requis (comme les piles et les files d'attente dynamiques), les listes liées sont un bon choix.
la source
En voici un rapide: la suppression d'articles est plus rapide.
la source
La liste des liens est particulièrement utile lorsque la collection est en constante augmentation et diminution. Par exemple, il est difficile d'imaginer essayer d'implémenter une file d'attente (ajouter à la fin, supprimer de l'avant) à l'aide d'un tableau - vous passeriez tout votre temps à déplacer les choses vers le bas. D'un autre côté, c'est trivial avec une liste chaînée.
la source
Outre l'ajout et la suppression du milieu de la liste, j'aime davantage les listes liées car elles peuvent croître et se réduire dynamiquement.
la source
Personne ne code plus sa propre liste de liens. Ce serait idiot. L'hypothèse selon laquelle l'utilisation d'une liste chaînée nécessite plus de code est tout simplement fausse.
De nos jours, la création 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 ++, basé sur la description de notre question, cela signifierait probablement un vecteur stl (
#include <vector>
).Par conséquent, le choix d'une liste chaînée par rapport à un tableau consiste entièrement à peser les différentes caractéristiques de chaque structure par rapport aux besoins de votre application. Surmonter la charge de programmation supplémentaire ne devrait avoir aucun impact sur la décision.
la source
C'est vraiment une question d'efficacité, la surcharge pour insérer, supprimer ou déplacer (où vous n'échangez pas simplement) des éléments dans une liste chaînée est minime, c'est-à-dire que l'opération elle-même est O (1), vers O (n) pour un tableau. Cela peut faire une différence significative si vous travaillez fortement sur une liste de données. Vous avez choisi vos types de données en fonction de la façon dont vous allez les utiliser et choisissez le plus efficace pour l'algorithme que vous utilisez.
la source
Les tableaux ont un sens là où le nombre exact d'éléments sera connu et où la recherche par index est logique. 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 pour 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.
la source
Arrays Vs Linked List:
la source
comme les tableaux sont de nature statique, toutes les opérations comme l'allocation de mémoire se produisent donc uniquement au moment de la compilation. Le processeur doit donc consacrer moins d'efforts à son exécution.
la source
Supposons que vous ayez un ensemble ordonné, que vous souhaitez également modifier en ajoutant et en supprimant des éléments. De plus, vous devez pouvoir conserver une référence à un élément de telle manière que vous puissiez ultérieurement obtenir 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, nous devons 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 de stocker des objets eux-mêmes. Sinon, vous ne pourrez pas insérer dans le tableau - si des objets sont intégrés dans le tableau, ils se déplaceront pendant les insertions et tous les pointeurs vers eux deviendront invalides. Il en va de même pour les index de tableau.
Votre premier problème, comme vous l'avez remarqué vous-même, est que la liste liée par insertion permet l'insertion dans O (1), mais un tableau nécessite généralement 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 ordonnée de type tableau où la lecture et l'écriture sont, au pire, logarithmiques.
Votre deuxième problème, plus grave, est qu'étant donné un élément, trouver l'élément suivant est O (n). Si l'ensemble n'a pas été modifié, vous pouvez conserver l'index de l'élément comme référence au lieu du pointeur, ce qui fait de find-next une opération O (1), mais comme il ne vous reste qu'un pointeur vers l'objet lui-même et aucun moyen pour déterminer son index actuel dans le tableau autrement qu'en scannant l'ensemble du "tableau". Il s'agit d'un problème insurmontable pour les tableaux - même si vous pouvez optimiser les insertions, il n'y a rien que vous puissiez faire pour optimiser l'opération de type find-next.
la source
Dans un tableau, vous avez le privilège d'accéder à n'importe quel élément en temps O (1). Ainsi, il convient pour des opérations telles que la recherche binaire, le tri rapide, etc. La liste liée, d'autre part, convient pour la suppression d'insertion comme dans le temps O (1). Les deux ont des avantages ainsi que des inconvénients et préférer l'un se résume à ce que vous souhaitez mettre en œuvre.
- La plus grande question est de savoir si nous pouvons avoir un hybride des deux. Quelque chose comme ce que python et perl implémentent sous forme de listes.
la source
Liste liée
C'est plus préférable quand il s'agit d'insertion! Fondamentalement, ce qui est fait, c'est qu'il traite du pointeur
1 -> 3 -> 4
Insérer (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 depuis Array
| 1 | 3 | 4 |
Insérer (2) | 1 | 3 | | 4 | | 1 | | 3 | 4 | | 1 | 2 | 3 | 4 |
Eh bien, 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? La baie est-elle 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 le tableau, nous devons effectuer shiftLeft ()
J'espère que cela pourra aider! :)
la source
La liste liée est plus une surcharge à gérer qu'une baie, elle nécessite également un stockage de mémoire supplémentaire, tous ces points sont convenus. Mais il y a quelques choses que le réseau ne peut pas faire. Dans de nombreux cas, supposons que vous vouliez un tableau de longueur 10 ^ 9, vous ne pouvez pas l'obtenir, car il doit y avoir un emplacement de mémoire continu. La liste liée pourrait être un sauveur ici.
Supposons que vous souhaitiez stocker plusieurs éléments avec des données, elles peuvent ensuite être facilement étendues dans la liste liée.
Les conteneurs STL ont généralement une implémentation de liste liée en arrière-plan.
la source
1- La liste liée est une structure de données dynamique qui peut donc croître et se réduire au moment de l'exécution en allouant et désallouant de la mémoire. Il n'est donc pas nécessaire de donner une taille initiale de la liste chaînée. L'insertion et la suppression de nœuds sont vraiment plus faciles.
2- La taille de la liste chaînée peut augmenter ou diminuer au moment de l'exécution, il n'y a donc pas de perte de mémoire. Dans le cas du tableau, il y a beaucoup de gaspillage de mémoire, comme si nous déclarons un tableau de taille 10 et y stockons seulement 6 éléments, alors l'espace de 4 éléments est gaspillé. Il n'y a pas un tel problème dans la liste chaînée car la mémoire n'est allouée qu'en cas de besoin.
3- Les structures de données telles que la pile et les files d'attente peuvent être facilement implémentées à l'aide d'une liste chaînée.
la source
La seule raison d'utiliser la liste chaînée est que l'insertion de l'élément est facile (suppression également).
L'inconvénient pourrait être que les pointeurs prennent beaucoup de place.
Et à propos de cela, le codage est plus difficile: Habituellement, vous n'avez pas besoin d'une liste liée de code (ou une seule fois), ils sont inclus dans STL et ce n'est pas si compliqué si vous devez encore le faire.
la source
je pense aussi que la liste de liens est mieux que les tableaux. parce que nous traversons dans la liste de liens mais pas dans les tableaux
la source
Selon votre langue, certains de ces inconvénients et avantages pourraient être pris en compte:
Langage de programmation C : lors de l'utilisation d'une liste chaînée (via des pointeurs de structure en général), une attention particulière doit être apportée pour éviter toute fuite de mémoire. Comme cela a été mentionné précédemment, les listes chaînées sont faciles à mélanger, car tout ce qui se faisait est de changer de pointeur, mais allons-nous nous rappeler de tout libérer?
Java : Java a un ramasse-miettes automatique, donc la fuite de mémoire ne sera pas un problème, mais les détails d'implémentation de ce qu'est une 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 est une procédure plus compliquée que certains utilisateurs de la langue ne s'y attendent.
la source
Pourquoi une liste chaînée sur un tableau? Eh bien, comme certains l'ont déjà dit, une plus grande vitesse d'insertions et de suppressions.
Mais peut-être que nous n'avons pas à vivre avec les limites des deux, 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, il n'est donc plus nécessaire de réorganiser le tableau. Pour alléger le fardeau des insertions ou des changements rapides de données, utilisez une liste chaînée pour cela. Ensuite, lorsque vous vous y référez, demandez à votre logique de rechercher d'abord l'un, puis l'autre. Ainsi, leur utilisation en combinaison vous offre 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 liée où le plus petit contient les 20, 50, 100 derniers éléments utilisés. Si celui dont vous avez besoin ne se trouve pas dans la liste ou le tableau lié le plus court, vous accédez au grand tableau. S'il y est trouvé, vous pouvez ensuite l'ajouter à la liste / tableau lié plus petit en supposant que `` les choses les plus récemment utilisées sont plus susceptibles d'être réutilisées '' (et oui, peut-être en supprimant 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 j'ai dû résoudre dans un module de vérification des autorisations de sécurité .ASP, avec facilité, élégance et vitesse impressionnante.
la source
Alors que beaucoup d'entre vous ont abordé les principaux avantages / différences de la liste chaînée par rapport au tableau, la plupart des comparaisons indiquent comment l'un est meilleur / pire que l'autre. vous pouvez faire un accès aléatoire dans le tableau mais pas possible dans la 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 bonne réponse devrait être de savoir comment la liste de liens serait préférée à la baie et vice-versa dans un déploiement d'application particulier. Supposons que vous souhaitiez implémenter une application de dictionnaire, que feriez-vous? Array: mmm il permettrait une récupération facile grâce à la recherche binaire et à d'autres algo de recherche .. mais laisse penser comment la liste de liens peut être meilleure .. Dites que vous voulez rechercher "Blob" dans le dictionnaire. Serait-il logique d'avoir une liste de liens de A-> B-> C-> D ---->
Maintenant, l'approche ci-dessus est-elle meilleure ou un ensemble plat de [Adam, Apple, Banana, Blob, Cat, Cave]? Serait-ce même possible avec array? Ainsi, un avantage majeur de la liste de liens est que vous pouvez avoir un élément non seulement pointant vers l'élément suivant, mais aussi vers une autre liste de liens / tableau / tas / ou tout autre emplacement de mémoire. Le tableau est une mémoire contigüe plate découpée en blocs de la taille de l'élément qu'il va stocker. La liste de liens, d'autre part, est un bloc d'unités de mémoire non contiguës (peut être de n'importe quelle taille et peut stocker n'importe quoi) et pointant vers chacune l'autre comme vous le souhaitez. Disons de même que vous créez une clé USB. Souhaitez-vous maintenant que les fichiers soient enregistrés sous forme de tableau ou de liste de liens? Je pense que vous avez l'idée de ce que je pointe :)
la source