Heapsort гарантированно O (N log N), что намного лучше, чем наихудший случай в быстрой сортировке. Heapsort не нужно больше памяти для размещения в другом массиве упорядоченных данных, как это требуется Mergesort. Так почему же коммерческие приложения придерживаются Quicksort? Что в Quicksort настолько особенное по сравнению с другими реализациями?
Я сам протестировал алгоритмы и увидел, что в Quicksort действительно есть что-то особенное. Он работает быстро, намного быстрее, чем алгоритмы Heap и Merge.
Секрет быстрой сортировки заключается в следующем: он почти не делает ненужных перестановок элементов. Своп требует много времени.
С Heapsort, даже если все ваши данные уже упорядочены, вы собираетесь поменять 100% элементов, чтобы упорядочить массив.
С Mergesort это еще хуже. Вы собираетесь записать 100% элементов в другой массив и записать его обратно в исходный, даже если данные уже упорядочены.
С помощью быстрой сортировки вы не меняете уже заказанное. Если ваши данные полностью упорядочены, вы почти ничего не поменяете! Хотя в худшем случае много шума из-за небольшого улучшения выбора pivot, кроме получения первого или последнего элемента массива, его можно избежать. Если вы получаете опору от промежуточного элемента между первым, последним и средним элементом, достаточно избежать наихудшего случая.
То, что превосходит Quicksort, - это не худший случай, но лучший случай! В лучшем случае вы делаете такое же количество сравнений, хорошо, но вы почти ничего не меняете. В среднем случае вы меняете часть элементов, но не все элементы, как в Heapsort и Mergesort. Вот что дает Quicksort лучшее время. Меньше своп, больше скорости.
Приведенная ниже реализация в C # на моем компьютере, работающая в режиме выпуска, превосходит Array.Sort по 3 секундам со средним поворотом и на 2 секунды с улучшенным поворотом (да, для получения хорошего поворота есть издержки).
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);
}