Против быстрой сортировки heapsort как

и quicksort и heapsort делают сортировку на месте. Что лучше? Какие приложения и случаи, в которых предпочтительнее?

11 ответов


http://www.cs.auckland.ac.nz / ~jmor159/PLDS210/qsort3.html имеет некоторый анализ.

кроме того, из Википедии:

самый прямой конкурент quicksort-это heapsort. Heapsort как это как правило, несколько медленнее, чем quicksort, но в худшем случае работает время всегда Θ(nlogn). Быстрой сортировки обычно быстрее, хотя остается вероятность худшего результата за исключением варианта introsort, которая переключается на heapsort, когда тяжелый случай обнаружен. Если это известно заранее что heapsort как будет необходимо, используя его сразу будет быстрее, чем ждать introsort в переключись на него.


Heapsort является o (n log N) гарантированным, что намного лучше, чем в худшем случае в Quicksort. Heapsort не нужно больше памяти для другого массива для размещения упорядоченных данных, как это необходимо Mergesort. Так почему же коммерческие приложения прилипают к Quicksort? Что у Quicksort есть такого особенного по сравнению с другими реализациями?

Я сам тестировал алгоритмы, и я видел, что у Quicksort есть что-то особенное. Он работает быстро, намного быстрее, чем куча и слияние алгоритмы.

секрет Quicksort: он почти не делает ненужные свопы элементов. Своп занимает много времени.

С Heapsort, даже если все ваши данные уже упорядочены, вы собираетесь поменять 100% элементов на порядок массива.

С Mergesort это еще хуже. Вы собираетесь записать 100% элементов в другой массив и записать его обратно в исходный, даже если данные уже упорядочены.

С Quicksort вы не меняете то, что есть уже заказал. Если ваши данные полностью упорядочены, вы почти ничего не меняете! Хотя существует много суеты о худшем случае, небольшое улучшение выбора pivot, любое другое, чем получение первого или последнего элемента массива, может избежать этого. Если вы получаете ось от промежуточного элемента между первым, последним и средним элементом, этого достаточно, чтобы избежать наихудшего случая.

то, что превосходит в Quicksort, не худший случай, но лучший случай! В лучшем случае вы делаете то же самое количество сравнений, хорошо, но вы почти ничего не меняете. В среднем случае вы меняете часть элементов, но не все элементы, как в Heapsort и Mergesort. Это то, что дает Quicksort лучшее время. Меньше обмена, больше скорости.

реализация ниже в C# на моем компьютере, работающем в режиме выпуска, бьет массив.Сортировка на 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);
}

для большинства ситуаций, имея быстрый против немного быстрее не имеет значения... вы просто не хотите иногда получить waayyy медленно. Хотя вы можете настроить QuickSort, чтобы избежать медленных ситуаций, вы теряете элегантность основного QuickSort. Так что, по большей части, я предпочитаю Хеапсорт... вы можете реализовать его в полной простой элегантности и никогда не получить медленный вид.

для ситуаций, когда вы хотите максимальную скорость в большинстве случаев, QuickSort может быть предпочтительнее HeapSort, но ни один из них не может быть правильным ответом. Для быстро критических ситуаций стоит внимательно изучить детали ситуации. Например, в некоторых из моих критически важных для скорости кодов очень часто данные уже отсортированы или почти отсортированы (это индексирование нескольких связанных полей, которые часто перемещаются вверх и вниз вместе или перемещаются вверх и вниз напротив друг друга, поэтому, как только вы сортируете по одному, другие сортируются или наоборот сортируются или закрываются... любой из которых может убить QuickSort). В этом случае я не реализовал ни то, ни другое... вместо этого я реализовал SmoothSort Дийкстры... вариант HeapSort, который является O (N), когда уже отсортирован или почти отсортирован... это не так элегантно, не слишком легко понять, но быстрый... читать http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF Если вы хотите что-то более сложное для кода.


гибриды Quicksort-Heapsort in-place тоже очень интересны, так как большинству из них требуется только N*log N сравнений в худшем случае (они оптимальны по отношению к первому члену асимптотики, поэтому они избегают наихудших сценариев Quicksort), o(log n) экстра-пространство и они сохраняют по крайней мере "половину" хорошего поведения Quicksort по отношению к уже упорядоченному набору данных. Чрезвычайно интересный алгоритм представлен Дикертом и Вайсом в http://arxiv.org/pdf/1209.4214v1.pdf:

  • выберите pivot p в качестве медианы случайной выборки элементов sqrt(n) (это можно сделать не более чем в 24 sqrt(n) сравнениях через алгоритм Tarjan&co или 5 sqrt(n) сравнениях через гораздо более запутанный алгоритм spider-factory Schonhage);
  • разделите массив на две части, как на первом шаге Quicksort;
  • сложите наименьшую часть и используйте o (log n) дополнительные биты для кодируйте кучу, в которой каждый левый ребенок имеет значение больше, чем его брат;
  • рекурсивно извлечь корень кучи, просеять лакуну, оставленную корнем, пока она не достигнет листа кучи, затем заполнить лакуну соответствующим элементом, взятым из другой части массива;
  • повторите над оставшейся неупорядоченной частью массива (если p выбран в качестве точной медианы, рекурсии вообще нет).

Comp. между quick sort и merge sort поскольку оба типа сортировки на месте, существует разница между временем выполнения wrost case времени выполнения wrost case для быстрой сортировки O(n^2) и для сортировки кучи это все еще O(n*log(n)) и для среднего объема данных быстрая сортировка будет более полезной. Поскольку это рандомизированный алгоритм, поэтому вероятность получения правильных ans. за меньшее время будет зависеть от позиции выбранного вами элемента pivot.

так а

хороший призыв: размеры L и G каждое чем 3s / 4

ошибка: один из L и G имеет размер больше, чем 3s/4

для небольшого количества мы можем пойти на сортировку вставки и для очень большого количества данных пойти на сортировку кучи.


хорошо, если вы перейдете на уровень архитектуры...мы используем структуру данных очереди в кэш-памяти.так что то, что когда-либо доступно в очереди, будет отсортировано.Как и в быстрой сортировке, у нас нет проблем с разделением массива на любую длину...но в сортировке кучи (с помощью массива) может случиться так, что родитель может не присутствовать в поддереве, доступном в кэше, а затем он должен принести его в кэш-память ...что отнимает много времени. Это quicksort лучше всего!!


heapsort как создает кучу, а затем повторно извлекает максимальный элемент. Худший вариант-O (N log n).

но если бы вы видели худший случай быстрая сортировка, то есть O (n2), вы бы поняли, что быстрая сортировка будет не очень хорошим выбором для больших данных.

таким образом, сортировка-интересная вещь; я считаю, что многие алгоритмы сортировки живут сегодня, потому что все они "лучшие" в своих лучших местах. Для экземпляр, bubble sort может выполнять быструю сортировку, если данные отсортированы. Или если мы знаем что-то о предметах для сортировки, то, вероятно, мы можем сделать лучше.

Это может не ответить на ваш вопрос напрямую, я подумал, что добавлю свои два цента.


Heapsort имеет преимущество иметь худший запущенный случай O(n*log (n)) поэтому в случаях, когда quicksort, вероятно, будет работать плохо (в основном сортированные наборы данных в целом), предпочтительнее heapsort.


сортировка кучи-безопасная ставка при работе с очень большими входами. Асимптотический анализ показывает, что порядок роста кучи в худшем случае равен Big-O(n logn), Что лучше, чем Quicksort's Big-O(n^2) в худшем случае. Однако,heapsort как несколько медленнее, на практике на большинстве машин, чем хорошо реализована быстрая сортировка. Heapsort также не является стабильным алгоритмом сортировки.

причина heapsort медленнее на практике, чем quicksort из-за лучшей местности ссылка ("https://en.wikipedia.org/wiki/Locality_of_reference") в quicksort, где элементы данных находятся в относительно близких местах хранения. Системы, которые демонстрируют сильную локальность ссылки, являются отличными кандидатами для оптимизации производительности. Сортировка кучи, однако, имеет дело с большими скачками. Это делает quicksort более благоприятным для более малых входных сигналов.


для меня существует очень фундаментальное различие между heapsort и quicksort: последний использует рекурсию. В рекурсивных алгоритмах куча растет вместе с числом рекурсий. Это не имеет значения, если n мало, но прямо сейчас я сортирую две матрицы с n=10^9 !!. Программа занимает почти 10 ГБ ОЗУ, и любая дополнительная память заставит мой компьютер начать замену на виртуальную дисковую память. Мой диск-это RAM-диск, но все же замена на него делает огромная разница в скорости. Поэтому в statpack, закодированном на C++, который включает регулируемые размерные матрицы, размер которых заранее неизвестен программисту, и непараметрический статистический вид сортировки я предпочитаю heapsort, чтобы избежать задержек при использовании с очень большими матрицами данных.


чтобы ответить на исходный вопрос и обратиться к некоторым другим комментариям здесь:

Я просто сравнил реализации выбора, быстрого, слияния и сортировки кучи, чтобы увидеть, как они складываются друг против друга. Ответ в том, что у всех есть свои недостатки.

TL; DR: Quick-лучший вид общего назначения (достаточно быстрый, стабильный и в основном на месте) Лично я предпочитаю сортировку кучи, хотя, если мне не нужен стабильный вид.

выбор-N^2-это действительно только для менее чем 20 элементов, потом она превзошла. Если ваши данные уже не отсортированы или очень, очень близки к этому. N^2 становится очень медленным, очень быстрым.

быстрый, по моему опыту, на самом деле не это быстро все время. Бонусы за использование быстрой сортировки в качестве общей сортировки, хотя это достаточно быстро и стабильно. Это также алгоритм на месте, но поскольку он обычно реализуется рекурсивно, он займет дополнительное пространство стека. Это также падает где-то между O(N log n) и O(n^2). Время на некоторых видах, похоже, подтверждает это, особенно когда значения попадают в узкий диапазон. Это намного быстрее, чем сортировка выбора по 10,000,000 элементам, но медленнее, чем слияние или куча.

Merge sort гарантируется O (N log n), поскольку его сортировка не зависит от данных. Он просто делает то, что он делает, независимо от того, какие ценности вы ему дали. Он также стабилен, но очень большие сорта могут выдуть ваш стек, если вы не будете осторожны реализация. Существуют некоторые сложные реализации сортировки слиянием на месте, но обычно для слияния значений требуется другой массив на каждом уровне. Если эти массивы живут в стеке, вы можете столкнуться с проблемами.

сортировка кучи-max O (N log n), но во многих случаях быстрее, в зависимости от того, как далеко вы должны переместить свои значения в глубокую кучу log n. Куча может быть легко реализована на месте в исходном массиве, поэтому ей не нужна дополнительная память, и она итеративна, поэтому нет беспокойство о переполнении стека во время рекурсии. The огромный недостатком сортировки кучи является то, что она не является стабильной, что означает, что это правильно, если вам это нужно.