La norme C++ 11 garantit que std::sort
a complexité O (n logn) dans le pire des cas . Ceci est différent de la garantie cas moyen en C++ 98/03, où std::sort
pourrait être implémenté avec Quicksort (peut être combiné avec un tri par insertion pour le petit n), qui a O (n ^ 2) dans le pire des cas (pour une entrée spécifique, telle qu'une entrée triée).
Y a-t-il eu des changements dans std::sort
implémentations dans différentes bibliothèques STL? Comment est le C++ 11 std::sort
implémenté dans différentes STL?
Parcourir les sources en ligne pour libstdc ++ et libc ++ , on peut voir que les deux bibliothèques utilisent la gamme complète des algorithmes de tri bien connus à partir d'une boucle principale d'intro-tri:
Pour std::sort
, Il existe une routine d'aide pour insertion_sort
(Un algorithme O(N^2)
mais avec une bonne constante de mise à l'échelle pour le rendre compétitif pour les petites séquences), plus un boîtier spécial pour sous-séquences de 0, 1, 2 et 3 éléments.
Pour std::partial_sort
, Les deux bibliothèques utilisent une version de heap_sort
(O(N log N)
en général), car cette méthode a un invariant Nice qui conserve une sous-séquence triée (elle a généralement un constante de mise à l'échelle plus grande pour le rendre plus cher pour un tri complet).
Pour std::nth_element
, Il existe une routine d'aide pour selection_sort
(Encore un algorithme O (N ^ 2) avec une bonne constante de claquage pour le rendre compétitif pour les petites séquences). Pour un tri régulier, insertion_sort
Domine généralement selection_sort
, Mais pour nth_element
L'invariant d'avoir les plus petits éléments correspond parfaitement au comportement de selection_sort
.
La question est de savoir comment [~ # ~] stl [~ # ~] dire std::sort
Le pire des cas est O (N log ( N)) , même s'il s'agit essentiellement d'un QuickSort . Le tri de STL est IntroSort . IntroSort est essentiellement un QuickSort, la différence introduite change la complexité du pire des cas.
Quel que soit le partitionnement que vous choisissez, il existe une séquence sur laquelle QuickSort s'exécutera O (N ^ 2) . Le partitionnement que vous choisissez ne fait que diminuer la probabilité que le pire des cas se produise. ( Sélection de pivot aléatoire , Médiane des trois, etc. )
EDIT: Merci à la correction de @ maxim1000. Tri rapide avec algorithme de sélection de pivot Médiane des médianes a O (N log (N)) pire complexité, mais en raison de la frais généraux, il introduit qu'il n'est pas utilisé dans la pratique. Il montre comment un bon algorithme de sélection peut théoriquement changer la complexité du pire des cas grâce à la sélection pivot.
IntroSort limite la ramification de QuickSort. C'est le point le plus important, cette limite est 2 * (log N) . Lorsque la limite est atteinte, IntroSort peut utiliser n'importe quel algorithme de tri présentant la pire complexité de O (N log (N)).
La ramification s'arrête lorsque nous avons des sous-problèmes O (log N). Nous pouvons résoudre chaque sous-problème O (n log n). (Les minuscules n représentent les tailles des sous-problèmes).
La somme de (n log n) est notre pire cas de complexité, maintenant.
Pour le pire des cas de QuickSort; supposons que nous avons un tableau déjà trié, et nous sélectionnons toujours le premier élément de ce tableau comme pivot. À chaque itération, nous ne supprimons que le premier élément. Si nous allions de cette façon jusqu'à la fin, ce serait O (N ^ 2) évidemment. Avec IntroSort, nous arrêtons QuickSort, lorsque nous atteignons une profondeur log (N) , puis nous utilisons HeapSort pour le tableau non trié restant.
16 -> 1 /**N**/
\
> 15 -> 1 /**N - 1**/
\
> 14 -> 1 /**N - 2**/
\
> 13 -> 1 /**N - log(N)**/
\
> 12 /**(HeapSort Now) (N - log(N)) log (N - log(N))**/
Résumez-les;
Jusqu'à l'arrêt de la branche, les opérations N + (N - 1) + ... + (N - log(N))
sont effectuées. Au lieu d'utiliser gauss pour résumer, nous pouvons simplement dire N + (N - 1) + ... + (N - log(N)) < N log(N)
.
La partie HeapSort est (N - log(N)) log(N - log(N)) < N log(N)
Complexité globale < 2 N log(N)
.
Étant donné que les constantes peuvent être omises, la complexité la plus défavorable de IntroSort est O (N log (N)) .
Informations ajoutées: [~ # ~] gcc [~ # ~] Le code source de l'implémentation STL est ici . Sort
la fonction est à la ligne 5461 .
Correction: * Microsoft .NET * sort L'implémentation est IntroSort depuis 2012. Les informations associées sont ici .