Heapsort est garanti O(N log N), ce qui est bien mieux que le pire cas de Quicksort. Heapsort n'a pas besoin de plus de mémoire pour un autre tableau pour mettre les données ordonnées comme c'est le cas pour Mergesort. Alors pourquoi les applications commerciales s'en tiennent-elles à Quicksort ? Qu'est-ce que Quicksort a de si spécial par rapport à d'autres implémentations ?
J'ai testé les algorithmes moi-même et j'ai constaté que Quicksort a vraiment quelque chose de spécial. Il fonctionne rapidement, beaucoup plus rapidement que les algorithmes Heap et Merge.
Le secret de Quicksort est le suivant : Il n'effectue pratiquement pas de permutations inutiles d'éléments. La permutation prend du temps.
Avec Heapsort, même si toutes vos données sont déjà ordonnées, vous allez échanger 100 % des éléments pour ordonner le tableau.
Avec Mergesort, c'est encore pire. Vous allez écrire 100% des éléments dans un autre tableau et les réécrire dans le tableau d'origine, même si les données sont déjà ordonnées.
Avec Quicksort, vous n'échangez pas ce qui est déjà commandé. Si vos données sont complètement ordonnées, vous n'échangez presque rien ! Bien qu'il y ait beaucoup d'agitation autour du pire cas, une petite amélioration sur le choix du pivot, autre que l'obtention du premier ou du dernier élément du tableau, peut l'éviter. Si vous obtenez un pivot à partir de l'élément intermédiaire entre le premier, le dernier et le milieu du tableau, cela suffit à éviter le pire des cas.
Ce qui est supérieur dans le Quicksort, ce n'est pas le pire des cas, mais le meilleur des cas ! Dans le meilleur des cas, vous faites le même nombre de comparaisons, d'accord, mais vous n'échangez presque rien. Dans le cas moyen, vous échangez une partie des éléments, mais pas tous les éléments, comme dans Heapsort et Mergesort. C'est ce qui permet à Quicksort d'être le plus rapide. Moins d'échanges, plus de vitesse.
L'implémentation ci-dessous en C# sur mon ordinateur, en mode release, bat Array.Sort de 3 secondes avec un pivot moyen et de 2 secondes avec un pivot amélioré (oui, il y a un surcoût pour obtenir un bon pivot).
static void Main(string[] args)
{
int[] arrToSort = new int[100000000];
var r = new Random();
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
Console.WriteLine("Press q to quick sort, s to Array.Sort");
while (true)
{
var k = Console.ReadKey(true);
if (k.KeyChar == 'q')
{
// quick sort
Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
QuickSort(arrToSort, 0, arrToSort.Length - 1);
Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
else if (k.KeyChar == 's')
{
Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
Array.Sort(arrToSort);
Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
}
}
static public void QuickSort(int[] arr, int left, int right)
{
int begin = left
, end = right
, pivot
// get middle element pivot
//= arr[(left + right) / 2]
;
//improved pivot
int middle = (left + right) / 2;
int
LM = arr[left].CompareTo(arr[middle])
, MR = arr[middle].CompareTo(arr[right])
, LR = arr[left].CompareTo(arr[right])
;
if (-1 * LM == LR)
pivot = arr[left];
else
if (MR == -1 * LR)
pivot = arr[right];
else
pivot = arr[middle];
do
{
while (arr[left] < pivot) left++;
while (arr[right] > pivot) right--;
if(left <= right)
{
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
left++;
right--;
}
} while (left <= right);
if (left < end) QuickSort(arr, left, end);
if (begin < right) QuickSort(arr, begin, right);
}
3 votes
Duplication possible de Supériorité du tri rapide sur le tri en tas