Quels sont les cas d'utilisation lorsqu'un algorithme de tri particulier est préféré aux autres - merge sort
vs quick sort
vs heap sort
vs intro sort
, etc.?
Existe-t-il un guide recommandé pour les utiliser, en fonction de la taille, du type de structure de données, de la mémoire et du cache disponibles et des performances de l'UC?
Tout d’abord, une définition, car c’est très important: Un type stable est une garantie 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 la performance moyenne d'une affaire compte plus que la performance dans le pire des cas. Un tri rapide est en moyenne O (N log N), O (N ^ 2) dans le pire des cas. Une bonne implémentation utilise un stockage auxiliaire O (log N) sous la forme d'espace de pile pour la récursion.
Fusionner le tri: Lorsque vous avez besoin d’un tri stable, O (N log N), c’est votre seule option. Son seul inconvénient est qu’elle utilise O(N) espace auxiliaire et qu’elle a une constante légèrement plus grande qu’un tri rapide. Il existe certaines sortes de fusion sur place, mais autant que je sache, elles ne sont ni stables ni pires que O (N log N). Même les types O (N log N) en place ont une constante tellement plus grande que le type de fusion simple et traditionnel qu'ils sont plus curieux que des algorithmes utiles.
Tri de tas: Lorsque vous n'avez pas besoin d'un tri stable et que vous vous souciez davantage des performances dans le pire des cas que des performances dans des cas moyens. Il est garanti qu’il s’agit de O (N log N) et utilise l’espace auxiliaire O(1), ce qui signifie que vous ne manquerez pas d’espace de pile ou d’espace de pile sur de très grandes entrées.
Introsort: Il s'agit d'un tri rapide qui bascule vers un tri de tas après une certaine profondeur de récursivité pour contourner le cas le plus défavorable de 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 de O (N log N). La seule raison d'utiliser un type de pile au lieu de cela est probablement dans les systèmes soumis à de graves contraintes de mémoire, où l'espace de pile O (log N) est pratiquement important.
Tri par insertion : Lorsque N est garanti être 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 une sorte stable.
Tri de bulles, tri de sélection : Lorsque vous faites quelque chose de rapide et de sale, vous ne pouvez pas utiliser simplement l'algorithme de tri de la bibliothèque standard. Le seul avantage qu'ils ont sur le tri par insertion est 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 O (N). Voici quelques cas où cela vaut la peine d'essayer:
Tri par comptage: Lorsque vous triez des nombres entiers avec une plage limitée.
Tri de base: Lorsque log (N) est significativement plus grand que K, où K est le nombre de chiffres de base.
Tri du seau: Quand vous pouvez garantir que votre saisie est distribuée de manière approximativement uniforme.
Vous trouverez un ensemble d'animations pour différents types de données et d'algorithmes sur sorting-algorithms.com
Quicksort est généralement le plus rapide en moyenne, mais il présente des comportements assez pervers dans le pire des cas. Donc, si vous devez garantir qu'aucune donnée incorrecte ne vous donne O(N^2)
, vous devriez l'éviter.
Merge-sort utilise davantage de mémoire, mais convient particulièrement au tri externe (c’est-à-dire aux gros fichiers qui ne rentrent pas dans la mémoire).
Heap-sort peut trier sur place et ne présente pas le pire comportement du second cas, mais est en moyenne plus lent que le tri rapide dans la plupart des cas.
Lorsque seuls des entiers appartenant à une plage restreinte sont impliqués, vous pouvez utiliser un type de tri de base pour le rendre très rapide.
Dans 99% des cas, vous pourrez utiliser les types de bibliothèque, généralement basés sur le tri rapide.
La page Wikipedia sur les algorithmes de tri présente un excellent tableau de comparaison.
http://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms
Les liens fournis vers les comparaisons/animations ne sont pas pris en compte 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 le faire, consultez la rubrique "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 aussi des images sympas comparant différentes algorithmes de tri.
@dsimcha a écrit: Compter le tri: Lorsque vous triez des entiers avec une plage limitée
Je changerais cela en:
Comptage: lorsque vous triez les entiers positifs (0 - Integer.MAX_VALUE-2 en raison du casier).
Vous pouvez toujours obtenir les valeurs max et min sous forme d'heuristique d'efficacité en temps linéaire.
Vous avez également besoin d’au moins n espace supplémentaire pour le tableau intermédiaire, qui 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 réellement à MAX_VALUE-2) voir: Les tableaux Java ont-ils une taille maximale?
J'expliquerais également que la complexité du type de base est O(wn) pour n clés qui sont des entiers de la taille de Word 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 la comparaison, 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)