Почему quicksort лучше, чем mergesort?

Я задал этот вопрос во время интервью. Они оба O (nlogn), и все же большинство людей используют Quicksort вместо Mergesort. Почему так?

29 ответов


Quicksort имеет O (n2) наихудшее время выполнения и O (nlogn) среднее время выполнения. Однако во многих сценариях сортировка слиянием лучше, потому что многие факторы влияют на время выполнения алгоритма, и при их объединении quicksort выигрывает.

в частности, часто цитируемая среда выполнения алгоритмов сортировки относится к числу сравнений или количеству свопов, необходимых для сортировки данных. Этот это действительно хороший показатель производительности, тем более, что он не зависит от базового аппаратного дизайна. Однако другие вещи – такие как локальность ссылки (т. е. мы читаем много элементов, которые, вероятно, находятся в кэше?) – также играют важную роль на текущем оборудовании. Quicksort, в частности, требует мало дополнительного пространства и показывает хорошую локальность кэша, и это делает его быстрее, чем сортировка слияния во многих случаях.

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

на практике многие современные реализации quicksort (в частности, libstdc++’s std::sort) на самом деле introsort, чей теоретический наихудший случай-O (nlogn), то же, что и сортировка слиянием. Он достигает этого, ограничивая глубину рекурсии и переключаясь на a другой алгоритм (heapsort как) как только он превышает logn.


Как отметили многие, средняя производительность для quicksort быстрее, чем mergesort. но Это верно только в том случае, если вы предполагаете постоянное время для доступа к любой части памяти по требованию.

в ОЗУ это предположение, как правило, не так уж плохо (это не всегда верно из-за кэшей, но это не так уж плохо). Однако, если ваша структура данных достаточно велика, чтобы жить на диске, то quicksort получает убил тот факт, что ваш средний диск что-то вроде 200 случайных поисков в секунду. Но на том же диске нет проблем с последовательным считыванием или записью мегабайт в секунду данных. Именно это и делает mergesort.

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

кроме того, если вам нужно сделать что-нибудь с наборами данных такого размера, подумайте хорошенько о том, как избежать попыток диска. Например, именно поэтому рекомендуется удалять индексы перед выполнением больших нагрузок данных в базах данных, а затем перестраивать индекс позже. Поддержание индекса во время загрузки означает постоянный поиск на диске. Напротив, если вы отбрасываете индексы, база данных может перестроить индекс, сначала отсортировав информацию, с которой нужно работать (используя mergesort, конечно!), а затем загрузить его в структуру данных BTREE для индекса. (B-деревьев естественно, хранятся в порядке, поэтому вы можете загрузить один из отсортированного набора данных с несколькими поисками на диск.)

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


на самом деле, QuickSort-Это O (n2). Его обычное дело время работы O (nlog (n)), но его в худшем случае is O (n2), который возникает при запуске его в списке, содержащем несколько уникальных элементов. Рандомизация принимает O (n). Конечно, это не меняет его худший случай, это просто предотвращает вредоносного пользователя от того, чтобы ваш вид занял много времени.

QuickSort более популярен, потому что он:

  1. на месте (MergeSort требует дополнительной линейной памяти для количества сортируемых элементов).
  2. имеет небольшую скрытую константу.

на Анимированные Алгоритмы Сортировки показывает ряд алгоритмов на 4 различных начальных условиях (случайных, почти отсортированных, обращенных, немногих уникальных) и может помочь.


" и все же большинство людей используют Quicksort вместо Mergesort. Почему так?"

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

да, Quicksort с тройным разделением, вероятно, является одним из лучших алгоритмов сортировки общего назначения, но нет никакого преодоления того факта, что "быстрая" сортировка звучит намного мощнее, чем "слияние".


Как отмечали другие, худший случай Quicksort-O(n^2), в то время как mergesort и heapsort остаются в O (nlogn). В среднем случае, однако, все три являются O (nlogn); поэтому они для подавляющего большинства случаев сопоставимы.

Что делает Quicksort лучше в среднем, так это то, что внутренний цикл подразумевает сравнение нескольких значений с одним, в то время как на двух других оба термина различны для каждого сравнения. Другими словами, Quicksort читает вдвое меньше, чем два других алгоритмы. На современных процессорах производительность сильно зависит от времени доступа, поэтому в конце концов Quicksort становится отличным первым выбором.


Я хотел бы добавить, что из трех алгоритмов, упомянутых до сих пор (mergesort, quicksort и сортировка кучи), только mergesort стабилен. То есть порядок не меняется для тех значений, которые имеют один и тот же ключ. В некоторых случаях это желательно.

но, по правде говоря, в практических ситуациях большинству людей нужна только хорошая средняя производительность и quicksort... быстро =)

все алгоритмы сортировки имеют свои взлеты и падения. См.статья в Википедии для сортировки алгоритмы для хорошего обзора.


му! Quicksort не лучше, он хорошо подходит для другого вида применения, чем mergesort.

Mergesort стоит рассмотреть, если скорость имеет значение, плохая худшая производительность не может быть допущена, и дополнительное пространство доступно.1

вы заявили, что они "они оба O(nlogn) [...]". Это неправильно. "Quicksort использует сравнения N^2/2 в худшем случае случай."1.

Однако самым важным свойством по моему опыту является простая реализация последовательного доступа, который вы можете использовать при сортировке при использовании языков программирования с императивной парадигмой.

1 Седжвик, Алгоритмы


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

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


с запись Википедии на Quicksort:

Quicksort также конкурирует с mergesort, другой рекурсивный вид алгоритм, но с преимуществом худший случай Θ (nlogn) running time. Mergesort-стабильный вид, в отличие от quicksort и heapsort, и могут быть легко адаптируется для работы связанной списки и очень большие списки, хранящиеся на носитель с медленным доступом, например диск хранилище или сетевое хранилище. Хотя quicksort можно записать в работайте над связанными списками, это будет часто страдайте от плохого выбора pivot без произвольный доступ. Основной недостаток из mergesort является то, что при работе для массивов требуется auxiliary (n) вспомогательный космос в лучшем случае, тогда как вариант quicksort с in-place использование секционирования и хвостовой рекурсии только space (logn) пространство. (Обратите внимание, что когда работа со связанными списками, mergesort требуется только небольшое, постоянное количество вспомогательного хранения.)


объяснение Википедии:

Как правило, quicksort значительно быстрее на практике, чем другие алгоритмы Θ(nlogn), потому что его внутренний цикл может быть эффективно реализован на большинстве архитектур, и в большинстве реальных данных можно сделать выбор дизайна, который минимизирует вероятность необходимости квадратичного времени.

быстрая сортировка

Mergesort

Я думаю, что есть также проблемы с объемом хранилища, необходимым для Mergesort (который является Ω (n)), которого нет в реализациях quicksort. В худшем случае они составляют одинаковое количество алгоритмического времени, но mergesort требует большего объема памяти.


Quicksort не лучше, чем mergesort. С O (N^2) (Худший случай, который редко случается) quicksort потенциально намного медленнее, чем O (nlogn) рода слияния. Quicksort имеет меньше накладных расходов, поэтому с небольшими N и медленными компьютерами это лучше. Но компьютеры сегодня так быстры, что дополнительные накладные расходы на слияние незначительны, а риск очень медленного быстрого слияния намного перевешивает незначительные накладные расходы на слияние в большинстве случаев.

кроме того, mergesort оставляет элементы с одинаковыми ключами в исходном порядке, полезный атрибут.


Я хотел бы добавить к существующим отличным ответам некоторую математику о том, как QuickSort выполняет при отклонении от лучшего случая и насколько это вероятно, что, я надеюсь, поможет людям понять немного лучше, почему случай O(n^2) не вызывает реальной озабоченности в более сложных реализациях QuickSort.

вне проблем с произвольным доступом есть два основных фактора, которые могут повлиять на производительность QuickSort, и они оба связаны с тем, как pivot сравнивается с данными были отсортированы.

1) небольшое количество ключей в данные. Набор данных одного и того же значения будет сортироваться в n^2 раз на ванильном 2-секционном QuickSort, потому что все значения, кроме местоположения pivot, каждый раз размещаются на одной стороне. Современные реализации устраняют это с помощью таких методов, как использование сортировки 3-секций. Эти методы выполняются для набора данных с одинаковым значением в O(n) раз. Таким образом, использование такой реализации означает, что ввод с небольшим количеством ключей фактически улучшает производительность больше не является проблемой.

2)Очень плохой выбор поворота может привести к худшему результату. В идеальном случае pivot всегда будет таким, что 50% данных меньше и 50% данных больше, так что вход будет разбит пополам во время каждой итерации. Это дает нам n сравнений и свопов раз log-2(n) рекурсии для O(n*logn) времени.

насколько неидеальный выбор поворота влияет на выполнение время?

рассмотрим случай, когда ось последовательно выбрана так, что 75% данных находится на одной стороне Оси. Это все еще O (n*logn), но теперь база журнала изменилась на 1/0.75 или 1.33. Отношение производительности при изменении базы всегда является константой, представленной log(2)/log (newBase). В этом случае эта константа равна 2.4. Таким образом, это качество выбора оси занимает в 2,4 раза больше времени, чем идеальное.

как быстро это сделать хуже?

не очень быстро, пока выбор оси не станет (последовательно) очень плохим:

  • 50% на одной стороне: (идеальный случай)
  • 75% на одной стороне: 2,4 раза как длиной
  • 90% на одной стороне: 6,6 раза как длиной
  • 95% на одной стороне: 13,5 раз длиннее
  • 99% на одной стороне: 69 раз длиннее

когда мы приближаемся к 100% с одной стороны, часть журнала выполнения приближается к n и целому выполнение асимптотически приближается к O (n^2).

в наивной реализации QuickSort такие случаи, как отсортированный массив (для 1-го элемента pivot) или обратно отсортированный массив (для последнего элемента pivot), будут надежно производить наихудшее время выполнения O(n^2). Кроме того, реализации с предсказуемым выбором оси могут подвергаться DoS-атаке данными, предназначенными для выполнения в наихудшем случае. Современные реализации избегают этого с помощью различных методов, таких как рандомизация данные перед сортировкой, выбором медианы из 3 случайно выбранных индексов и т. д. С этой рандомизации в миксе, у нас есть 2 случая:

  • малый набор данных. Наихудший случай разумно возможен, но O (n^2) не является катастрофическим, потому что n достаточно мал, что N^2 также мал.
  • большой набор данных. Худшем случае можно в теории но не на практике.

насколько вероятно, что мы увидим ужасное представление?

в шансы исчезающе мала. Рассмотрим своего рода 5000 значений:

наша гипотетическая реализация выберет ось, используя медиану из 3 случайно выбранных индексов. Мы будем рассматривать оси, которые находятся в диапазоне 25% -75%, как "хорошие", а оси, которые находятся в диапазоне 0% -25% или 75% -100%, как"плохие". Если вы посмотрите на распределение вероятностей с использованием медианы из 3 случайных индексов, каждая рекурсия имеет 11/16 шанс получить хороший поворот. Давайте сделаем 2 консервативные (и ложные) предположения для упрощения математики:

  1. хорошие оси всегда точно на 25%/75% разделены и работают на 2.4*идеальный случай. Мы никогда не получаем идеальный раскол или любой раскол лучше, чем 25/75.

  2. плохие повороты всегда худший случай и по существу ничего не способствуют решению.

наша реализация QuickSort остановится на n=10 и переключится на сортировку вставки, поэтому нам требуется 22 25% / 75% сводные разделы, чтобы разбить входное значение 5,000 вниз, что далеко. (10*1.333333^22 > 5000) или, мы требуем 4990 худшем случае поворачивается. Имейте в виду, что если мы накапливаем 22 хороших поворота на любая точка тогда сортировка будет завершена, поэтому в худшем случае или что-нибудь рядом с ней требуется очень невезение. Если бы нам потребовалось 88 рекурсий, чтобы фактически достичь 22 хороших поворотов, необходимых для сортировки до n=10, это было бы 4*2.4*идеальным случаем или примерно в 10 раз больше времени выполнения идеала случай. Как вероятно, что мы бы не достичь требуемых 22 хороших поворотов после 88 рекурсий?

биномиальные распределения вероятностей может ответить на это, и ответ составляет около 10^-18. (n-88, k-21, p-0,6875) ваш пользователь примерно в тысячу раз чаще будет поражен молнией за 1 секунду, необходимую для нажатия кнопки [сортировать], чем они увидят, что сортировка элементов 5,000 выполняется хуже чем 10*идеальный случай. Этот шанс получает меньше по мере увеличения набора данных. Вот некоторые размеры массива и их соответствующие шансы работать дольше 10 * идеально:

  • массив из 640 элементов: 10^-13 (требуется 15 хороших точек поворота из 60 попыток)
  • массив из 5000 элементов: 10^-18 (требуется 22 хороших поворота из 88 попыток)
  • массив из 40 000 элементов: 10^-23 (требуется 29 хороших поворотов из 116)

помните, что это с 2 консервативными предположениями, которые хуже чем реальность. Таким образом, фактическая производительность еще лучше, а баланс оставшейся вероятности ближе к идеалу, чем нет.

наконец, как упоминали другие, даже эти абсурдно маловероятные случаи могут быть устранены путем переключения на сортировку кучи, если стек рекурсии идет слишком глубоко. Таким образом, TLDR заключается в том, что для хороших реализаций QuickSort худший случай на самом деле не существует потому что он был спроектирован и выполнение завершается в O(n * logn) время.


ответ слегка наклонится в сторону quicksort w.r.t к изменениям, внесенным с помощью DualPivotQuickSort для примитивных значений . Он используется в JAVA 7 сортировать java.утиль.Массивы

It is proved that for the Dual-Pivot Quicksort the average number of
comparisons is 2*n*ln(n), the average number of swaps is 0.8*n*ln(n),
whereas classical Quicksort algorithm has 2*n*ln(n) and 1*n*ln(n)
respectively. Full mathematical proof see in attached proof.txt
and proof_add.txt files. Theoretical results are also confirmed
by experimental counting of the operations.

вы можете найти имплментацию JAVA7 здесь - http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/Arrays.java

дальнейшее удивительное чтение на DualPivotQuickSort - http://permalink.gmane.org/gmane.comp.java.openjdk.core-libs.devel/2628


хотя они оба находятся в одном классе сложности, это не означает, что они оба имеют одинаковую среду выполнения. Quicksort обычно быстрее, чем mergesort, просто потому, что проще закодировать плотную реализацию, и операции, которые она делает, могут идти быстрее. Это потому, что quicksort обычно быстрее, что люди используют его вместо mergesort.

! Я лично часто буду использовать mergesort или вариант quicksort, который деградирует до mergesort, когда quicksort делает плохо. Помнить. Quicksort-это только O (n log n) on в среднем. В худшем случае это O (n^2)! Mergesort всегда O (N log n). В случаях, когда производительность или оперативность в реальном времени являются обязательными и ваши входные данные могут поступать из вредоносного источника,вы не должны использовать простой quicksort.

Quicksort имеет лучшую среднюю сложность случая, но в некоторых приложениях это неправильный выбор. Quicksort уязвим к атакам отказа в обслуживании. Если злоумышленник может выбрать вход для сортировки, он может легко построить набор, который занимает наихудшую временную сложность o(n^2).

средняя сложность случая Mergesort и наихудшая сложность случая одинаковы, и как таковая не страдает той же проблемой. Это свойство merge-sort также делает его превосходным выбором для системы реального времени-именно потому, что нет патологических случаев, которые заставляют его работать намного медленнее.

по этим причинам я больший поклонник Mergesort, чем Quicksort.


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

Мне интересно, почему это так редко можно увидеть основание или ведро рода. Они O (n), по крайней мере, в связанных списках, и все, что требуется, это некоторый метод преобразования ключа в порядковый номер. (струны и поплавки работают просто отлично.)

Я думаю, что причина связана с тем, как информатику преподают. Мне даже пришлось продемонстрировать моему преподавателю по алгоритмическому анализу, что сортировка действительно возможна быстрее, чем O(N log(n)). (У него было доказательство, что вы не можете сравнение сортировка быстрее, чем O(N log(n)), что верно.)

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

изменить: На самом деле, вот еще более порочный способ сортировки поплавков как целых чисел:http://www.stereopsis.com/radix.html. Обратите внимание, что трюк с битом можно использовать независимо от того, какой алгоритм сортировки вы фактически используете...


трудно сказать.Худшим из слияний является n(log2n)-n+1,что является точным, если n равно 2^k (я уже доказал это).И для любого n он находится между (N lg n-n + 1) и(N lg n + n + O (lg n)).Но для quickSort лучше всего nlog2n (также n равно 2^k).Если вы разделите Mergesort на quickSort, он будет равен единице, когда n бесконечно.Так что, как будто худший случай слияния лучше,чем лучший случай QuickSort, почему мы используем quicksort?Но помните, MergeSort не на месте,он требует 2n memeroy пространство.И MergeSort также нужно сделать много копий массива, которые мы не включаем в анализ алгоритма.Одним словом, MergeSort действительно быстрее,чем quicksort в theroy,но на самом деле вам нужно учитывать пространство мемов,стоимость копирования массива, слияние медленнее, чем быстрая сортировка.Однажды я провел эксперимент,в котором мне дали 1000000 цифр на java случайным классом,и это заняло 2610ms от mergesort, 1370ms от quicksort.


почему Quicksort хорошо?

  • QuickSort принимает N^2 в худшем случае и средний случай NlogN. В худшем случае данные сортируются. Это может быть смягчено случайным перемешиванием перед началом сортировки.
  • QuickSort не требует дополнительной памяти, которая берется путем сортировки слиянием.
  • если набор данных большой и есть идентичные элементы, сложность Quicksort уменьшается с помощью 3-х стороннего раздела. Больше нет идентичных предметов лучше род. Если все элементы идентичны, он сортирует в линейном времени. [Это реализация по умолчанию в большинстве библиотек]

всегда ли Quicksort лучше, чем Mergesort?

Не совсем так.

  • Mergesort стабилен, но Quicksort-нет. Поэтому, если вам нужна стабильность вывода, вы должны использовать Mergesort. Стабильность необходима в много практических применений.
  • память дешева в наше время. Поэтому, если дополнительная память используется Mergesort не является критичным для вашего приложения, нет никакого вреда в использовании Mergesort.

Примечание: в java массивы.функция sort () использует Quicksort для примитивных типов данных и Mergesort для типов данных объектов. Поскольку объекты потребляют накладные расходы памяти, поэтому добавление небольших накладных расходов для Mergesort не может быть проблемой для точки зрения производительности.

ссылка: смотрите видео QuickSort из Неделя 3, Принстонский курс алгоритмов на Корсеры


быстрая сортировка-худший случай O (n^2), однако средний случай последовательно выполняет сортировку слиянием. Каждый алгоритм O (nlogn), но вы должны помнить, что, говоря о большом O, мы оставляем более низкие факторы сложности. Быстрая сортировка имеет значительные улучшения по сравнению с сортировкой слияния, когда дело доходит до постоянных факторов.

Merge sort также требует o (2n) памяти, в то время как быстрая сортировка может быть выполнена на месте(требуется только O (n)). Это еще одна причина, по которой quick sort обычно предпочтительнее сортировки слиянием.

дополнительная информация:

худший случай быстрой сортировки происходит, когда ось выбрана плохо. Рассмотрим следующий пример:

[5, 4, 3, 2, 1]

Если pivot выбран как наименьшее или наибольшее число в группе, то быстрая сортировка будет выполняться в O(n^2). Вероятность выбора элемента, который находится в наибольшем или наименьшем 25% списка, равна 0,5. Это дает алгоритму 0,5 шанса быть хороший поворот. Если мы используем типичный алгоритм выбора пивота (скажем, выбор случайного элемента), у нас есть 0,5 шанса выбрать хороший пивот для каждого выбора пивота. Для коллекций большого размера вероятность всегда выбирать плохую ось равна 0,5 * n. Исходя из этой вероятности, быстрая сортировка эффективна для среднего (и типичного) случая.


в merge-sort общий алгоритм:

  1. сортировка левого поддерева
  2. сортировка правильного суб-массива
  3. объединить 2 отсортированных суб-массива

на верхнем уровне объединение 2 отсортированных суб-массивов включает в себя работу с N элементами.

на один уровень ниже, каждая итерация шага 3 включает в себя работу с N / 2 элементами, но вы должны повторить этот процесс дважды. Таким образом, вы все еще имеете дело с 2 * N/2 == N элементы.

на один уровень ниже, вы объединяете 4 * N/4 == n элементов и так далее. Каждая глубина в рекурсивном стеке включает слияние одного и того же количества элементов во всех вызовах этой глубины.

рассмотрим алгоритм быстрой сортировки:

  1. Выберите точку поворота
  2. поместите точку поворота в правильное место в массиве, со всеми меньшими элементами слева и большими элементами справа
  3. сортировка левый подмассив
  4. сортировка справа-subarray

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

на один уровень ниже, вы имеете дело с 2 суб-массивами, которые имеют объединенный размер N-1 (т. е. вычитают более раннюю точку поворота). Вы выбираете точку поворота для каждого суб-массива, которая доходит до 2 дополнительных точек поворота точки.

на один уровень ниже, вы имеете дело с 4 суб-массивами с комбинированным размером N-3 по тем же причинам, что и выше.

Затем N-7... Тогда N-15... Тогда N-32...

глубина рекурсивного стека остается примерно одинаковой (logN). С merge-sort вы всегда имеете дело с слиянием N-элементов на каждом уровне рекурсивного стека. Однако при быстрой сортировке количество элементов, с которыми вы имеете дело, уменьшается по мере продвижения вниз по стеку. Для например, если вы посмотрите на глубину в середине рекурсивного стека, количество элементов, с которыми вы имеете дело, равно N - 2^((logN)/2)) == N - sqrt(N).

отказ от ответственности: при объединении-сортировка, потому что вы разделяете массив на 2 точно равных куска каждый раз, рекурсивная глубина точно logN. При быстрой сортировке, поскольку ваша точка поворота вряд ли будет точно посередине массива, глубина рекурсивного стека может быть немного больше logN. Я не сделал математику, чтобы увидеть, как большую роль этот фактор и фактор, описанный выше, фактически играют в сложности алгоритма.


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


в отличие от сортировки слиянием быстрая сортировка не использует вспомогательные помещения. В то время как сортировка слиянием использует вспомогательное пространство O(n). Но сортировка слиянием имеет наихудшую временную сложность O(nlogn), тогда как наихудшая временная сложность быстрой сортировки-O (n^2), которая происходит, когда массив уже отсортирован.


небольшие дополнения к сортировкам quick vs merge.

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

например, мы сортируем элементы по сетевому протоколу на удаленном сервере.

кроме того, в пользовательских контейнерах, таких как "связанный список", нет преимущества быстрой сортировки.
1. Объединить сортировку в связанном списке, не нужна дополнительная память. 2. Доступ к элементам в быстрой сортировке не является последовательным (в памяти)


что-то рассмотреть память. Для Mergesort требуется дополнительный массив, например "массив рабочей области". Если ваша память едва достаточно велика для хранения исходного массива, то mergesort не будет работать.


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

В отличие от массивов, в liked list мы можем вставлять элементы посередине с O(1) пространством и O(1) временем, поэтому операция слияния в merge sort может быть реализована без дополнительного пространства. Однако выделение и удаление дополнительного пространства для массивов отрицательно влияет на время выполнения слияния род. Сортировка слиянием также способствует связанному списку, поскольку доступ к данным осуществляется последовательно, без особого случайного доступа к памяти.

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

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

EDIT: я только что узнал, что сортировка слияния худший/лучший/avg случай всегда nlogn, но быстрая сортировка может варьироваться от n2(худший случай, когда элементы уже отсортированы) до nlogn(avg/лучший случай, когда pivot всегда делит массив на две половины).


Это довольно старый вопрос, но так как я недавно имел дело с обоими, вот мой 2c:

слияние сортировки требуется в среднем ~ N log N сравнения. Для уже (почти) отсортированных отсортированных массивов это сводится к 1/2 N log N, так как при слиянии мы (почти) всегда выбираем "левую" часть 1/2 N раз, а затем просто копируем правые 1/2 N элементов. Кроме того, я могу предположить, что уже отсортированный вход делает предсказатель ветвей процессора блестящим, но угадывает почти все ветви правильно, таким образом предотвращение стойл трубопровода.

быстрая сортировка в среднем требует ~ 1.38 N log N сравнения. Он не очень выигрывает от уже отсортированного массива с точки зрения сравнений (однако это происходит с точки зрения свопов и, вероятно, с точки зрения предсказаний ветвей внутри CPU).

мои тесты на довольно современном процессоре показывают следующее:

когда функция сравнения является функцией обратного вызова (например, в реализации qsort () libc) quicksort медленнее, чем mergesort на 15% на случайном входе и 30% для уже отсортированного массива для 64-битных целых чисел.

с другой стороны, если сравнение не является обратным вызовом, мой опыт заключается в том, что quicksort outperforms mergesort до 25%.

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

поэтому, возможно, суть в следующем :если сравнение дорого (например, функция обратного вызова, сравнение строк, сравнение многих частей структуры в основном переход ко второму-третьему-четвертому "Если", чтобы сделать разницу) - шансы на то, что вы будете лучше с сортировкой слияния. Для более простых задач quicksort будет быстрее.

это сказало, что все ранее сказанное верно: - Quicksort может быть N^2, но Sedgewick утверждает, что хорошая рандомизированная реализация имеет больше шансов на то, что компьютер, выполняющий сортировку, будет поражен молнией, чем идти N^2 - Mergesort требует дополнительного пространства


в C / C++ land, когда я не использую контейнеры stl, я обычно использую quicksort, потому что он построен во время выполнения, в то время как mergesort нет.

поэтому я считаю, что во многих случаях, это просто путь наименьшего сопротивления.

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


одна из причин более философская. Quicksort-это философия сверху вниз. С сортировки n элементов, существует N! возможности. С 2 разделами m & n-m, которые являются взаимоисключающими, количество возможностей уменьшается на несколько порядков. м! * (Н-м)! меньше на несколько порядков, чем N! один. представьте 5! против 3! *2!. 5! в 10 раз больше возможностей, чем 2 разделы 2 и 3 каждый . и экстраполировать на 1 миллион факториалов против 900K!*100к! и поэтому вместо того, чтобы беспокоиться о создании любого порядка в пределах диапазона или раздела просто установите порядок на более широком уровне в разделах и уменьшите возможности внутри раздела. Любой порядок, установленный ранее в пределах диапазона, будет нарушен позже, если сами разделы не являются взаимоисключающими.

любой подход к порядку снизу вверх, такой как сортировка слияния или сортировка кучи, похож на подход рабочих или сотрудников, где вы начинаете сравнивать на микроскопическом уровне рано. Но этот порядок неизбежно будет потерян, так как как только элемент между ними будет найден позже. Эти подходы очень стабильны и чрезвычайно предсказуемы, но делают определенный объем дополнительной работы.

быстрая сортировка похожа на управленческий подход, когда человек изначально не беспокоится о каком-либо заказе , а только о выполнении широкого критерия без учета порядка. Затем разделы сужаются, пока вы не получите отсортированный набор. Реальная проблема в Quicksort заключается в поиске раздела или критерия в темноте, когда вы ничего не знаете о элементы для сортировки. Вот почему нам нужно либо потратить некоторое усилие, чтобы найти медианное значение, либо выбрать 1 наугад или произвольный "управленческий" подход . Чтобы найти идеальную медиану, может потребоваться значительное количество усилий и снова приведет к глупому подходу снизу вверх. Поэтому Quicksort говорит просто выбрать случайный поворот и надеяться, что он будет где-то посередине или сделать некоторую работу, чтобы найти медиану 3, 5 или что-то еще, чтобы найти лучшую медиану, но не планируют быть совершенными и не тратьте время на сначала заказ. Кажется, это хорошо, если вам повезло или иногда ухудшается до n^2, Когда вы не получаете медиану, но просто рискуете. В любом случае данные случайны. право. Поэтому я больше согласен с логическим подходом top - >down quicksort и выясняется, что шанс, который он принимает в отношении выбора и сравнения pivot, который он сохраняет ранее, кажется, работает лучше больше раз, чем любой дотошный и тщательный стабильный подход bottom ->up, такой как сортировка слияния. Но!--1-->