À partir de l'extrait wiki de la balise liste de liens :
Une liste chaînée est une structure de données dans laquelle les éléments contiennent des références à l'élément suivant (et éventuellement à l'élément précédent). Les listes liées offrent O (1) insertion et suppression à n'importe quelle position , O(1) concaténation de liste, et = O(1) accès aux positions avant (et éventuellement arrière) ainsi que O(1) accès suivant aux éléments. L'accès aléatoire a O(N) complexité et n'est généralement pas implémenté.
(c'est moi qui souligne)
J'ai été surpris de lire ceci - comment peut la liste insérer à un index aléatoire avec une complexité plus faible que simplement lecture cet index?
J'ai donc regardé le code source de Java.util.LinkedList
. La méthode add(int, E)
est:
public void add(int index, E element) {
addBefore(element, (index==size ? header : entry(index)));
}
La méthode addBefore(E, Entry<E>
est simplement une réaffectation de pointeur, mais il y a aussi la méthode entry(int)
:
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
Même avec l'optimisation à mi-taille, la boucle for
ici (l'un ou l'autre) me semble être un signe mort que cette méthode (et donc add(int, E)
) fonctionne dans un minimum pire- scénario de cas O(n) temps, et certainement pas à temps constant.
Qu'est-ce que je rate? Suis-je incompris de la notation big-O?
Eh bien, ils prennent en charge les insertions à temps constant à des positions arbitraires - mais seulement si vous avez un pointeur vers l'entrée de liste après quoi ou devant que vous souhaitez insérer quelque chose. Bien sûr, cela ne fonctionnera pas si vous n'avez que l'index, mais ce n'est pas ce que vous faites habituellement dans du code optimisé.
En Java, vous pouvez aussi le faire, mais niquement en utilisant un itérateur de liste .
Cette propriété des listes chaînées est leur plus grand avantage par rapport aux listes d'arrays ou plus - par exemple, si vous souhaitez supprimer un utilisateur de la liste d'utilisateurs d'un salon de discussion, vous pouvez stocker un pointeur vers la position de l'utilisateur dans la liste d'utilisateurs de l'utilisateur afin que , quand il veut quitter la pièce, cela peut être implémenté comme une opération O(1)
.
Cela est dû au fait que l'article que vous lisez considérait "accéder à cet index" comme une opération distincte. L'article suppose que vous êtes déjà à l'index que vous souhaitez effectuer add (int, E).
De conclure:
opération d'insertion ou de suppression = O (1)
Recherche d'un nœud à ne index = O (n)
L'opération de liaison le nouveau nœud à n'importe quel nœud est O(1) mais l'opération de recherche (aide à la boucle) la l'indice concerné est certainement O (n).
Il n'y a pas de magie;)
La page wiki que vous citez dit:
O (1) insérer et retirer à n'importe quel position
Ensuite, vous demandez:
J'ai été surpris de lire ceci - comment la liste peut-elle être insérée au hasard index
C'est là que réside la confusion: les termes position et index ne sont pas utilisés pour signifier la même chose. Le wiki parle d'un itérateur ou d'un pointeur, pas d'un index.