web-dev-qa-db-fra.com

Quand utiliser une liste chaînée sur un tableau / une liste de tableaux?

J'utilise beaucoup de listes et de tableaux, mais je n'ai pas encore rencontré de scénario dans lequel la liste de tableaux ne pourrait pas être utilisée aussi facilement, sinon plus facilement que la liste liée. J'espérais que quelqu'un pourrait me donner des exemples de cas où la liste chaînée est nettement meilleure.

157
faceless1_14

Les listes chaînées sont préférables aux tableaux quand:

  1. vous avez besoin d'insertions/suppressions en temps constant dans la liste (comme dans l'informatique en temps réel où la prévisibilité du temps est absolument essentielle)

  2. vous ne savez pas combien d'éléments seront dans la liste. Avec les tableaux, vous devrez peut-être déclarer à nouveau et copier de la mémoire si le tableau devient trop gros

  3. vous n'avez pas besoin d'un accès aléatoire à des éléments

  4. vous voulez pouvoir insérer des éléments au milieu de la liste (comme une file d'attente prioritaire)

Les tableaux sont préférables quand:

  1. vous avez besoin d'un accès indexé/aléatoire aux éléments

  2. vous connaissez le nombre d'éléments dans le tableau à l'avance afin de pouvoir allouer la quantité de mémoire correcte pour le tableau

  3. vous avez besoin de vitesse pour parcourir tous les éléments en séquence. Vous pouvez utiliser le calcul mathématique du pointeur sur le tableau pour accéder à chaque élément, tandis que vous devez rechercher le nœud en fonction du pointeur de chaque élément de la liste chaînée, ce qui peut entraîner des erreurs de page pouvant entraîner des problèmes de performances.

  4. la mémoire est une préoccupation. Les tableaux remplis utilisent moins de mémoire que les listes chaînées. Chaque élément du tableau correspond uniquement aux données. Chaque nœud de liste liée requiert les données ainsi qu'un (ou plusieurs) pointeurs vers les autres éléments de la liste liée.

Les listes de tableaux (comme celles de .Net) vous apportent les avantages des tableaux, mais allouent dynamiquement des ressources pour que vous ne craigniez plus la taille de la liste et que vous puissiez supprimer des éléments à n'importe quel index sans effort ni relance. mélanger les éléments autour. En termes de performances, les artistes sont plus lents que les tableaux bruts.

238
Lamar

Les tableaux ont un accès aléatoire O(1)), mais il est très coûteux d’ajouter ou de supprimer des éléments dans.

Les listes chaînées sont vraiment peu coûteuses pour ajouter ou supprimer des éléments n'importe où et pour itérer, mais l'accès aléatoire est O (n).

54
Dustin
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)

Les listes de tableaux sont bonnes pour l'écriture, les lectures uniques ou les ajouts, mais pas pour les ajouts/suppressions au début ou au milieu.

17
Vpn_talent

Pour ajouter aux autres réponses, la plupart des implémentations de liste de tableaux réservent une capacité supplémentaire à la fin de la liste afin que de nouveaux éléments puissent être ajoutés à la fin de la liste dans O(1) time. Lorsque la capacité d'une liste de tableaux est dépassée, un nouveau tableau plus grand est alloué en interne et tous les anciens éléments sont copiés. Habituellement, le nouveau tableau est le double de la taille de l'ancien. Cela signifie que en moyenne, l'ajout de nouveaux éléments à la fin d'une liste de tableaux est une opération O(1) dans ces implémentations. Ainsi, même si vous ne connaissez pas le nombre d'éléments à l'avance, une liste de tableaux peut toujours être plus rapide qu'une liste chaînée pour ajouter des éléments, à condition que vous les ajoutiez à la fin. Évidemment, insérer de nouveaux éléments à des emplacements arbitraires dans une liste de tableaux reste une opération O(n).

L'accès aux éléments d'une liste est également plus rapide qu'une liste chaînée, même si les accès sont séquentiels. En effet, les éléments du tableau sont stockés dans une mémoire contiguë et peuvent être facilement mis en cache. Les nœuds de liste chaînée peuvent potentiellement être dispersés sur de nombreuses pages différentes.

Je recommanderais d'utiliser uniquement une liste chaînée si vous savez que vous allez insérer ou supprimer des éléments à des emplacements arbitraires. Les listes de tableaux seront plus rapides pour pratiquement tout le reste.

14
Jay Conrod

L'avantage des listes apparaît si vous devez insérer des éléments au milieu et ne souhaitez pas commencer à redimensionner le tableau et à en déplacer les éléments.

Vous avez raison de dire que ce n'est généralement pas le cas. J'ai eu quelques cas très spécifiques comme celui-là, mais pas trop.

7
Uri

Ce sont les implémentations les plus utilisées de Collection.

ArrayList:

  • insérer/supprimer à la fin en général O(1) dans le pire des cas, O (n)

  • insérer/supprimer au milieu O (n)

  • récupérer n'importe quelle position O (1)

LinkedList:

  • insérer/supprimer dans n'importe quelle position O(1) (remarque si vous avez une référence à l'élément)

  • récupérer au milieu O (n)

  • récupérer le premier ou le dernier élément O (1)

Vecteur: ne l'utilisez pas. C'est une ancienne implémentation similaire à ArrayList mais avec toutes les méthodes synchronisées. Ce n'est pas l'approche correcte pour une liste partagée dans un environnement multithreading.

HashMap

insérer/supprimer/récupérer par clé dans O (1)

TreeSet insérer/supprimer/contient dans O (journal N)

HashSet insérer/supprimer/contient/taille en O (1)

2

Tout dépend du type d'opération que vous effectuez en effectuant une itération. Toutes les structures de données ont un compromis entre temps et mémoire et, en fonction de nos besoins, nous devons choisir le bon DS. Il existe donc des cas où LinkedList est plus rapide qu'un tableau et inversement. Considérons les trois opérations de base sur les structures de données.

  • Recherche

Étant donné que array est une structure de données basée sur un index, la recherche de array.get (index) prendra O(1) alors que la liste chaînée n'est pas index DS, vous devrez donc aller jusqu'à index, où index <= n, n est la taille de la liste chaînée, donc array est plus rapide que la liste chaînée lorsque l'accès aux éléments est aléatoire.

Q. Alors quelle est la beauté derrière cela?

Comme les tableaux sont des blocs de mémoire contigus, de gros morceaux d’entre eux seront chargés dans le cache lors du premier accès, ce qui le rend relativement rapide à accéder aux éléments restants du tableau. Autant que nous accédons aux éléments de la matrice, la localité de référence augmente aussi, donc moins de prises La localité du cache fait référence aux opérations présentes dans le cache et s’exécute donc beaucoup plus rapidement que dans la mémoire. En principe, array maximise les chances d’accès séquentiel aux éléments dans le cache. Bien que les listes chaînées ne fassent pas nécessairement partie de blocs de mémoire contigus, rien ne garantit que les éléments qui apparaissent séquentiellement dans la liste sont en réalité rangés les uns en face des autres dans la mémoire, ce qui signifie moins de résultats de cache, par exemple. plus de cache manquants car nous avons besoin de lire à partir de la mémoire pour chaque accès d'élément de liste liée, ce qui augmente le temps nécessaire pour y accéder et réduit les performances. Par conséquent, si nous effectuons plus d'opérations d'accès aléatoire, le tableau sera rapide comme expliqué ci-dessous.

  • Insertion

Ceci est facile et rapide dans LinkedList car l’insertion est O(1) opération dans LinkedList (en Java) par rapport à array, considérons le cas où array est plein, nous devons copier le contenu dans un nouveau array si array se remplit, ce qui rend l'insertion d'un élément dans ArrayList de O(n) dans le pire des cas, tandis que ArrayList doit également mettre à jour son index si vous insérez quelque chose n'importe où sauf à la fin du tableau, en cas de liste chaînée pas besoin de le redimensionner, il vous suffit de mettre à jour les pointeurs.

  • Suppression

Cela fonctionne comme des insertions et mieux dans LinkedList que array.

2
Harleen

En réalité, la localité mémoire a une influence considérable sur les performances dans le traitement réel.

L’utilisation accrue de la diffusion en continu sur disque dans le traitement des "données volumineuses" par rapport à l’accès aléatoire montre à quel point la structuration de votre application en ce sens peut améliorer considérablement les performances à plus grande échelle.

S'il existe un moyen quelconque d'accéder à un tableau de manière séquentielle, c'est de loin le plus performant. Concevoir avec cet objectif comme objectif devrait au moins être pris en compte si la performance est importante.

1
user3150186

J'ai effectué des analyses comparatives et découvert que la classe list est en réalité plus rapide que LinkedList pour l'insertion aléatoire:

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();
        }
    }
}

Il faut 900 ms pour la liste liée et 100 ms pour la classe de liste.

Il crée des listes de nombres entiers ultérieurs. Chaque nouvel entier est inséré après un nombre aléatoire déjà présent dans la liste. Peut-être que la classe List utilise quelque chose de mieux qu'un simple tableau.

1
Emil Albert

Je pense que la principale différence est de savoir si vous devez fréquemment insérer ou supprimer des éléments en haut de la liste.

Avec un tableau, si vous supprimez quelque chose du haut de la liste, la complexité est égale à o(n)), car tous les index des éléments du tableau devront être décalés.

Avec une liste chaînée, c’est o(1) car il suffit de créer le nœud, de réaffecter la tête et d’affecter la référence à next comme à la tête précédente.

Lors de l'insertion ou de la suppression fréquentes à la fin de la liste, les tableaux sont préférables car la complexité sera de 0 (1), aucune réindexation n'est requise, mais pour une liste chaînée, elle sera o(n) = parce que vous devez aller de la tête au dernier nœud.

Je pense que la recherche dans les listes chaînées et les tableaux sera o (log n) car vous utiliserez probablement une recherche binaire.

0
Curious_goat

Hmm, Arraylist peut être utilisé dans les cas suivants:

  1. vous ne savez pas combien d'éléments seront présents
  2. mais vous devez accéder à tous les éléments au hasard grâce à l'indexation

Par exemple, vous devez importer et accéder à tous les éléments d'une liste de contacts (dont la taille vous est inconnue).

0
Raghu

1) Comme expliqué ci-dessus, les opérations d'insertion et d'extraction donnent de bonnes performances (O (1)) dans LinkedList par rapport à ArrayList (O (n)). Par conséquent, s’il existe une exigence d’ajouts et de suppressions fréquents dans une application, alors LinkedList est le meilleur choix.

2) Les opérations de recherche (méthode) sont rapides dans Arraylist (O (1)) mais pas dans LinkedList (O (n)). Par conséquent, s'il y a moins d'opérations d'ajout et de suppression et davantage d'exigences d'opérations de recherche, ArrayList serait votre meilleur pari.

0
Avanish Kumar

Utilisez la liste liée pour le tri de base sur les tableaux et pour les opérations polynomiales.

0
gizgok