L'algorithme de tri rapide peut être divisé en étapes suivantes
Identifiez le pivot.
Partitionnez la liste liée en fonction du pivot.
Divisez la liste chaînée récursivement en 2 parties.
Maintenant, si je choisis toujours le dernier élément comme pivot, alors l'identification de l'élément pivot (1ère étape) prend du temps .
Après avoir identifié l'élément pivot, nous pouvons stocker ses données et les comparer avec tous les autres éléments pour identifier le point de partition correct (2e étape). Chaque comparaison prendra fois que nous stockons les données de pivot et chaque swap prend O ( 1 ) . Donc, au total, il faut O ( n ) temps pour n éléments.
La relation de récurrence est donc:
qui est O ( n log n ) qui est le même qu'en tri par fusion avec une liste chaînée.
Alors, pourquoi le tri par fusion est-il préféré au tri rapide pour les listes liées?
Réponses:
Le modèle d'accès à la mémoire dans Quicksort est aléatoire, l'implémentation prête à l'emploi est également en place, il utilise donc de nombreux swaps si les cellules pour obtenir le résultat ordonné.
En même temps, le tri par fusion est externe, il nécessite un tableau supplémentaire pour renvoyer le résultat ordonné. Dans le tableau, cela signifie une surcharge d'espace supplémentaire, dans le cas d'une liste liée, il est possible d'extraire de la valeur et de commencer la fusion des nœuds. L'accès est de nature plus séquentielle.
Pour cette raison, le tri rapide n'est pas un choix naturel pour la liste liée, tandis que le tri par fusion en tire un grand avantage.
La notation Landau pourrait (plus ou moins, car Quicksort est toujours ) d'accord, mais la constante est beaucoup plus élevée.O(n2)
Dans le cas moyen, les deux algorithmes sont en donc le cas asymptotique est le même, mais la préférence est strictement due à la constante cachée et parfois la stabilité est le problème (quicksort est intrinsèquement instable, mergsort est stable).O(nlogn)
la source
Vous pouvez trier rapidement les listes liées, mais vous serez très limité en termes de sélection de pivot, vous limitant aux pivots près du début de la liste, ce qui est mauvais pour les entrées presque triées, sauf si vous voulez faire une boucle sur chaque segment deux fois (une fois pour pivot et une fois pour la partition). Et vous devrez conserver une pile des limites de partition pour les listes que vous devez encore trier. Cette pile peut atteindre lorsque la sélection de pivot est mauvaise avec la complexité temporelle passant à O ( n 2 ) .O(n) O(n2)
Le tri par fusion sur les listes liées peut être exécuté en utilisant uniquementO(1)
C'est l'algorithme que le noyau Linux utilise pour trier ses listes chaînées. Cependant, avec quelques optimisations supplémentaires, comme ignorer le
previous
pointeur pendant toutes les opérations de fusion, sauf la dernière.la source
Vous pouvez écrire le tri par fusion, le tri par partition, le tri par arborescence et comparer les résultats
Il est assez facile de l' arbre d'écriture sorte si vous permettre un peu d' espace supplémentaire
pour l' arbre trier chaque nœud de liste chaînée doit avoir deux pointeurs , même si l' on liste chaînée sorte séparément
Dans liste chaînée je préfère insérer et supprimer au lieu d'échanger
la partition Hoare ne peut être effectuée que pour la liste doublement liée
Ce code a besoin d'améliorations
Tout d'abord, nous devons limiter le stockage supplémentaire aux besoins de récursivité,
puis nous devons essayer de remplacer la récursivité par l'itération
Si nous voulons améliorer encore l'algorithme, nous devons utiliser un arbre d'auto-équilibrage
la source
Tri rapide
Peut-être que je montrerai les étapes du quicksort
Si la liste contient plusieurs nœuds
première sous-liste contient des nœuds avec des clés inférieures à la clé pivot La
deuxième sous-liste contient des nœuds avec des clés égales à la clé pivot
troisième sous-liste contient des nœuds avec des clés supérieures à la clé pivot
Annonce 1.
Si nous voulons choisir le pivot rapidement, le choix est limité
Nous pouvons choisir le nœud principal ou le nœud arrière
Notre liste doit avoir un pointeur sur le nœud si nous voulons que notre pivot
soit accessible rapidement sinon nous devons rechercher le nœud
Annonce 2.
Nous pouvons utiliser les opérations de file d'attente pour cette étape.
Fist, nous retirons le nœud de la liste chaînée d'origine,
comparons sa clé avec la clé pivot et le nœud de mise en file d'attente à la sous-liste correcte.
listes sont créées à partir des nœuds existants et il n'est pas nécessaire de
allouer de la mémoire pour les nouveaux nœuds.
Le pointeur vers le nœud de queue sera utile car les opérations de file d'attente
et la concaténation s'exécutent plus rapidement avec la présence de ce pointeur
la source