Quand chaque algorithme de tri est-il utilisé? [fermé]


170

Quels sont les cas d'utilisation où un algorithme de tri particulier est préféré aux autres - tri par fusion vs QuickSort vs heapsort vs 'intro sort', etc.?

Existe-t-il un guide recommandé pour leur utilisation en fonction de la taille, du type de structure de données, de la mémoire et du cache disponibles et des performances du processeur?


Un ensemble d'animations pour différents types de données et d'algorithmes peut être trouvé à <a href=" sorting-algorithms.com/"> sorting-algorithms.com </ a >
Chip Uni

2
Un guide comme bigocheatsheet.com pour ce genre de choses serait greaaaat
K - La toxicité dans le SO augmente.

@ChipUni voici le lien fixe: toptal.com/developers/sorting-algorithms
eric

2
Pourquoi cette question est-elle close!?
Arvand

Réponses:


316

Tout d'abord, une définition, car c'est assez important: un tri stable est celui qui garantit de ne pas réorganiser les éléments avec des clés identiques.

Recommandations:

Tri rapide: lorsque vous n'avez pas besoin d'un tri stable et que les performances moyennes des cas sont plus importantes que les pires performances. Un tri rapide est O (N log N) en moyenne, O (N ^ 2) dans le pire des cas. Une bonne implémentation utilise le stockage auxiliaire O (log N) sous la forme d'espace de pile pour la récursivité.

Tri par fusion: lorsque vous avez besoin d'un tri stable, O (N log N), il s'agit de votre seule option. Le seul inconvénient est qu'il utilise l'espace auxiliaire O (N) et a une constante légèrement plus grande qu'un tri rapide. Il existe des types de fusion sur place, mais AFAIK ils ne sont pas tous stables ou pires que O (N log N). Même les tris O (N log N) en place ont une constante tellement plus grande que l'ancien tri de fusion simple qu'ils sont plus des curiosités théoriques que des algorithmes utiles.

Tri en tas: lorsque vous n'avez pas besoin d'un tri stable et que vous vous souciez plus des performances des pires cas que des performances moyennes des cas. Il est garanti qu'il est O (N log N) et utilise l'espace auxiliaire O (1), ce qui signifie que vous ne manquerez pas inopinément d'espace de tas ou de pile sur de très grandes entrées.

Introsort: Il s'agit d'un tri rapide qui passe à un tri de tas après une certaine profondeur de récursivité pour contourner le pire des cas O (N ^ 2) du tri rapide. C'est presque toujours mieux qu'un simple tri rapide, car vous obtenez le cas moyen d'un tri rapide, avec des performances garanties O (N log N). La seule raison d'utiliser un tri de tas au lieu de cela est probablement dans les systèmes à forte contrainte de mémoire où l'espace de pile O (log N) est pratiquement significatif.

Tri par insertion : lorsque N est garanti petit, y compris comme cas de base d'un tri rapide ou d'un tri par fusion. Bien que ce soit O (N ^ 2), il a une très petite constante et est un tri stable.

Tri par bulles, tri par sélection : lorsque vous faites quelque chose de rapide et de sale et pour une raison quelconque, vous ne pouvez pas simplement utiliser l'algorithme de tri de la bibliothèque standard. Le seul avantage de ceux-ci par rapport au tri par insertion est d'être légèrement plus facile à mettre en œuvre.


Tris sans comparaison: dans certaines conditions assez limitées, il est possible de briser la barrière O (N log N) et de trier en O (N). Voici quelques cas où cela vaut la peine d'essayer:

Tri par comptage: lorsque vous triez des entiers avec une plage limitée.

Tri par radix: lorsque log (N) est nettement plus grand que K, où K est le nombre de chiffres de base.

Tri par compartiment: lorsque vous pouvez garantir que votre entrée est distribuée à peu près uniformément.


1
Si je me souviens bien, le tri en tas a également un temps d'exécution très prévisible en ce sens qu'il y a peu de variation entre différentes entrées de même taille, mais c'est moins intéressant que sa limite d'espace constant. Je trouve également que le tri par insertion est le plus facile à implémenter des types n ^ 2, mais c'est peut-être juste moi. Enfin, vous pouvez également mentionner le tri Shell, qui est presque aussi simple à implémenter que le tri par insertion mais qui offre de meilleures performances, mais pas toujours n log n.
JaakkoK

29
N'oubliez pas Bogosort ! ;-)
Alex Brasetvik

2
+1 Très intéressant. Voudriez-vous expliquer comment vous pouvez "garantir ... une distribution à peu près uniforme". pour Bucket Sort?
Sam Overton

2
Pourquoi l'introsort serait-il beaucoup plus lent que le tri rapide? Le seul surcoût est de compter la profondeur de récursivité, qui devrait être négligeable. Il ne change que lorsque la récursivité est beaucoup plus profonde qu'elle ne devrait l'être dans un bon cas de tri rapide.
dsimcha

2
Vous oubliez de mentionner que le meilleur cas de tri à bulles est O (n)!
Tara

33

Quicksort est généralement le plus rapide en moyenne, mais il a des comportements assez désagréables dans le pire des cas. Donc, si vous devez garantir qu'aucune mauvaise donnée ne vous donne O(N^2), vous devez l'éviter.

Le tri par fusion utilise de la mémoire supplémentaire, mais est particulièrement adapté au tri externe (c'est-à-dire aux fichiers volumineux qui ne rentrent pas dans la mémoire).

Le tri en tas peut trier sur place et n'a pas le pire comportement quadratique, mais il est en moyenne plus lent que le tri rapide dans la plupart des cas.

Là où seuls des entiers dans une plage restreinte sont impliqués, vous pouvez utiliser une sorte de tri de base pour le rendre très rapide.

Dans 99% des cas, vous serez d'accord avec les types de bibliothèques, qui sont généralement basés sur un tri rapide.


6
+1: Pour "Dans 99% des cas, vous serez d'accord avec les sortes de bibliothèques, qui sont généralement basées sur un tri rapide".
Jim G.19

Le pivotement aléatoire donne à Quicksort un runtime de O (nlogn) à toutes fins pratiques, sans avoir besoin de garanties sur les mauvaises données. Je ne pense vraiment pas que quiconque implémente un tri rapide O (n ^ 2) pour un code de production.
MAK

2
MAK, sauf, disons, la bibliothèque standard C qsort? ( google.com/codesearch/… ) - sur lequel reposent la plupart des types de "code de production"
Eli Bendersky

Le tri des bibliothèques n'est généralement pas basé sur le tri rapide, car il n'est pas stable. Presque tous les langages supérieurs (sauf pour C) fournissent un tri stable. Dans la plupart des cas, je sais que vous avez besoin d'un tri stable, ou du moins déterministe.
12431234123412341234123


3

Ce que les liens fournis vers des comparaisons / animations ne prennent pas en compte, c'est lorsque la quantité de données dépasse la mémoire disponible - à quel point le nombre de passages sur les données, c'est-à-dire les coûts d'E / S, domine le temps d'exécution. Si vous avez besoin de faire cela, lisez sur le "tri externe" qui couvre généralement les variantes des tris de fusion et de tas.

http://corte.si/posts/code/visualisingsorting/index.html et http://corte.si/posts/code/timsort/index.html ont également quelques images sympas comparant divers algorithmes de tri.


0

@dsimcha a écrit: Tri de comptage: lorsque vous triez des entiers avec une plage limitée

Je changerais cela en:

Tri par comptage: Lorsque vous triez des entiers positifs (0 - Integer.MAX_VALUE-2 en raison du casier).

Vous pouvez toujours obtenir les valeurs max et min comme heuristique d'efficacité en temps linéaire.
Vous avez également besoin d'au moins n espace supplémentaire pour le tableau intermédiaire et il est évidemment stable.

/**
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

(même si cela permet en fait MAX_VALUE-2) voir: Les tableaux Java ont-ils une taille maximale?

J'expliquerais également que la complexité du tri de base est O (wn) pour n clés qui sont des entiers de taille de mot w. Parfois, w est présenté comme une constante, ce qui rendrait le tri de base meilleur (pour n suffisamment grand) que les meilleurs algorithmes de tri basés sur des comparaisons, qui effectuent tous des comparaisons O (n log n) pour trier n clés. Cependant, en général, w ne peut pas être considéré comme une constante: si toutes les n clés sont distinctes, alors w doit être au moins log n pour qu'une machine à accès aléatoire puisse les stocker en mémoire, ce qui donne au mieux une complexité temporelle O (n log n). (de wikipedia)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.