Почему быстрая сортировка лучше, чем сортировка слиянием?

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

Это не очень хороший вопрос для собеседования. Реальные данные не перетасовываются: они часто содержат много порядка, который может использовать интеллектуальная сортировка, и хотя ни один алгоритм не делает это автоматически, легче взломать сортировку слиянием, чем быструю сортировку. qsort GNU libc, list.sort Python и Array.prototype.sort в JavaScript Firefox - все это усовершенствованные виды слияния. (GNU STL sort вместо этого использует Introsort, но это может быть связано с тем, что в C++ подкачка потенциально выигрывает перед копированием.)

Jason Orendorff 10.12.2009 04:01

@ Джейсон Орендорф: Почему это "easier to hack a mergesort to do it than a quicksort"? Вы можете процитировать какой-нибудь конкретный пример?

Lazer 03.04.2010 14:55

@eSKay Сортировка слиянием начинается с группировки исходных данных в отсортированные подмассивы. Если массив изначально содержит некоторые уже отсортированные регионы, вы можете сэкономить много времени, просто обнаружив их наличие до начала. И вы можете сделать это за O (n) раз. Конкретные примеры см. В исходном коде трех упомянутых мной проектов! Лучшим примером может быть Python Timsort, подробно описанный здесь: svn.python.org/view/python/trunk/Objects/… и реализованный в svn.python.org/view/python/trunk/Objects/….

Jason Orendorff 05.04.2010 23:28

@JasonOrendorff: Не уверен, что я согласен с вашим аргументом в пользу того, что сортировку слиянием легче изменить, чтобы воспользоваться преимуществами уже отсортированных разделов. Шаг разделения быстрой сортировки можно тривиально изменить, чтобы впоследствии проверить, отсортированы ли оба результирующих раздела, и остановить рекурсию, если это так. Это потенциально удваивает количество сравнений, но не меняет временную сложность этого шага O (n).

j_random_hacker 15.07.2012 08:06

@j_random_hacker: Если на этапе разбиения проверяется, отсортировано ли оно как после, вероятно, уже слишком поздно и частичный порядок уже нарушен.

Mooing Duck 24.10.2014 02:46

@MooingDuck: Я не понимаю. После выбора точки поворота и разбиения вокруг нее во время быстрой сортировки (время O (n)) вы можете затем сравнить каждый элемент с предыдущим элементом, чтобы увидеть, отсортирован ли уже этот (под) массив, снова за время O (n). Если это так, вам не нужно продолжать рекурсию - все просто!

j_random_hacker 28.10.2014 03:00

@MooingDuck: Вместо этого вы можете выполнить уже отсортированную проверку перед разделением. Я не могу сказать, что будет быстрее, но оба способа верны.

j_random_hacker 28.10.2014 03:03

@j_random_hacker: да, я имел в виду именно это. Но учтите: {10, 2, 3, 4, 5, 6, 7, 8, 1, 9} Несмотря на то, что они уже почти полностью отсортированы, проверка ни перед разделом, ни после него не найдет его. И раздел испортит это до того, как последующие вызовы это проверит. Между тем, сортировка слиянием проверяет отсортированные последовательности на этапах деления до того, как какая-либо из них будет перемещена, а умные будут искать такие прогоны специально на этапе деления (см .: Сортировка по Тиму)

Mooing Duck 28.10.2014 03:13

@MooingDuck: А, значит, вы имеете в виду, что quicksort может с пользой пропустить длинные уже отсортированные прогоны - да, я согласен. Посмотрев на страницу Timsort Wikipedia, я понял, что вы (и Джейсон) имели в виду сейчас. Это выглядит очень эффективно, чтобы очистить почти весь существующий частичный порядок.

j_random_hacker 28.10.2014 03:27

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

liamvictor 16.09.2008 13:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
377
10
201 545
28
Перейти к ответу Данный вопрос помечен как решенный

Ответы 28

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

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

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

На самом деле, есть реализация QuickSort, которая в худшем случае равна O (n * log (n)), а не O (n ^ 2).

jfs 17.09.2008 02:17

Это также зависит от архитектуры компьютера. Quicksort выигрывает от кеширования, а MergeSort - нет.

Cristian Ciupitu 28.09.2008 05:53

@ J.F. Себастьян: Скорее всего, это реализации интросорта, а не быстрой сортировки (интросорт запускается как быстрая сортировка и переключается на динамическую сортировку, если он собирается перестать быть n * log (n)).

CesarB 20.10.2008 01:50

Вы можете реализовать сортировку слиянием на месте.

Marcin 20.10.2008 13:25

@Dark Shikari, я внес некоторые исправления, отредактировав ваш ответ напрямую. См. Эту страницу для наглядной демонстрации пояснений: sorting-algorithms.com/quick-sort В итоге быстрая сортировка по умолчанию хуже всех в списке с несколькими уникальными элементами, а не в списке с обратной сортировкой. Следовательно, этого нельзя избежать, обеспечив случайный порядок списка.

Ash 13.11.2009 07:37

@Dark Shikari, почему вы говорите, что сортировка слиянием требует больше памяти по сравнению с быстрой сортировкой?

ThE uSeFuL 19.04.2013 06:00

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

Stan R. 19.06.2013 09:03

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

Clearer 21.12.2014 22:07

@CristianCiupitu Разве не должно быть наоборот? Сортировка слиянием обращается к данным последовательно, в результате чего строки кэша (как целевого, так и целевого) остаются в памяти. С другой стороны, Quicksort может быть повсюду. Нельзя сказать, что сортировка слиянием лучше, но я не думаю, что это один из ее недостатков.

Aidiakapi 24.04.2015 14:10

@CristianCiupitu Я знаю, что Quicksort использует кеш, но я не согласен с вашим утверждением, что сортировка слиянием не работает. Сортировка слиянием обычно сохраняет оба массива в кеше, и он в значительной степени получает доступ к данным исключительно последовательно, что является лучшим случаем для кеша. Быстрая сортировка имеет преимущество перед сортировкой слиянием из-за многих факторов, таких как отсутствие необходимости во вторичном массиве, сценарии двойного поворота. Но локальность кеша - сильная сторона обоих алгоритмов.

Aidiakapi 05.05.2015 02:29

@Marcin Реализация сортировки слиянием на месте, как известно, сложна и часто приводит к большему количеству свопов, которые перекрывают повышение эффективности за счет уменьшения использования памяти см. penguin.ewu.edu/cscd300/Topic/AdvSorting/MergeSorts/…

david_adler 25.10.2015 23:11

быстрая сортировка НЕ ​​на месте. на месте означает O (1) дополнительной памяти. для быстрой сортировки требуется рекурсия или стек и поэтому используется дополнительная память O (log n)

piotrek 17.11.2017 02:57

«На самом деле QuickSort - это O (n2)» - на самом деле это неверно.

Jim Balter 03.11.2018 15:39

@piotrek, да, он есть, потому что дополнительная память предназначена для рекурсии, а не данных. А на 64-битной машине QS никогда не требует больше 64 слов дополнительной памяти, которая выделяется заранее. (Более 64 не будут использоваться даже для плохого набора данных, если сначала будет отсортирована более короткая подпоследовательность, что и делают все практические реализации.)

Jim Balter 03.11.2018 15:45

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

Nick Gallimore 15.11.2018 09:19

См. Здесь сортировку слиянием на месте, которая выполняется за O (n ^ 2) stackoverflow.com/questions/2571049/…

Nick Gallimore 15.11.2018 09:21
вместо сортировки слиянием, которая выполняется за O (n ^ 2) -- I think you misread or mistyped that. In-place merge sort can be done in O(n * (logn)^2), and there are a couple of examples on that page.
Jim Balter 28.11.2018 15:09

@nfgallimore при использовании медианы 3 для выбора точки поворота? Будет ли это по-прежнему O (n ^ 2)? - Да, конечно ... должно быть ясно, что какой элемент выбран в качестве точки поворота, не может изменить регистр наихудший. версии чистой быстрой сортировки являются O (nlogn) - такого зверька нет. Но нечистые версии, такие как IntroSort и pdqsort (github.com/orlp/pdqsort), имеют наихудший случай O (nlogn).

Jim Balter 28.11.2018 15:10

@jimbalter, можете ли вы привести пример набора данных, где медиана 3 находится в O (nlogn)

Nick Gallimore 24.02.2019 22:10

@nfgallimore quicksort со средней точкой 3 в упорядоченном списке, конечно же, O (nlogn). Возможно, вы имели в виду не O (nlogn) или это O (n ^ 2) ... Я не буду приводить пример, потому что его создание требует усилий. Примеры не актуальны ... логика есть.

Jim Balter 25.02.2019 08:06

Да, я имел в виду не в O (nlogn)

Nick Gallimore 26.02.2019 05:51

@JimBalter «да, это на месте, потому что дополнительная память предназначена для рекурсии, а не для данных» - это ложная дихотомия. Рекурсия используется для хранения данных (параметров функции). Если вы не можете выполнить рекурсию, вам все равно понадобится явный стек. Потребляется такой же объем памяти.

user519179 10.09.2019 18:34

^ Это утомительно. Как известно каждому честному человеку, быстрая сортировка «на месте», потому что она записывает отсортированный результат в исходный буфер. Абсурдное заявление piotek о том, что он не на месте, потому что он использует дополнительную память, неверно как семантически, так и на практике. Как я уже сказал, «на 64-битной машине QS никогда не требует более 64 слов дополнительной памяти, которая выделяется заранее». Это нерекурсивная функция, которая использует 64 слова в стеке. Пространство стека уже выделено ... дополнительная память не используется и код выделения отсутствует; указатель стека просто перемещается на 64 слова.

Jim Balter 10.09.2019 22:59

Рандомизация занимает O (n)? O (n log n) в ожидании.

Null_Space 31.01.2021 05:14

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

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

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

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

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

Как Quicksort имеет лучшую среднюю сложность дела? Они оба O (nlgn). Я бы сказал, что злоумышленник не будет предоставлять входные данные для какого-либо алгоритма сортировки ... но, чтобы не предполагать безопасность посредством неизвестности, предположим, что он мог. Хотя время работы n ^ 2 хуже, чем nlgn, это не настолько хуже, что веб-сервер выйдет из строя из-за одной атаки. Фактически, аргумент DOS практически равен нулю, потому что любой веб-сервер уязвим для DDOS-атаки, и для злоумышленника более вероятно, что злоумышленник будет использовать распределенную сеть хостов, все TCP SYN-лавинная.

CaTalyst.X 16.04.2013 00:44

«Quicksort имеет лучшую среднюю сложность дела» - нет, это не так.

Jim Balter 03.11.2018 16:09

От запись в Википедии о Quicksort:

Quicksort also competes with mergesort, another recursive sort algorithm but with the benefit of worst-case Θ(nlogn) running time. Mergesort is a stable sort, unlike quicksort and heapsort, and can be easily adapted to operate on linked lists and very large lists stored on slow-to-access media such as disk storage or network attached storage. Although quicksort can be written to operate on linked lists, it will often suffer from poor pivot choices without random access. The main disadvantage of mergesort is that, when operating on arrays, it requires Θ(n) auxiliary space in the best case, whereas the variant of quicksort with in-place partitioning and tail recursion uses only Θ(logn) space. (Note that when operating on linked lists, mergesort only requires a small, constant amount of auxiliary storage.)

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

Typically, quicksort is significantly faster in practice than other Θ(nlogn) algorithms, because its inner loop can be efficiently implemented on most architectures, and in most real-world data it is possible to make design choices which minimize the probability of requiring quadratic time.

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

Сортировка слиянием

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

Худший случай быстрой сортировки - O (n), mergesort O (n log n) - так что здесь большая разница.

paul23 04.09.2016 15:43

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

paul23 04.09.2016 16:54

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

Jim Balter 03.11.2018 16:13

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

Тем не мение! Я лично часто использую сортировку слиянием или вариант быстрой сортировки, который ухудшается до сортировки слиянием, когда быстрая сортировка работает плохо. Помните. Быстрая сортировка - это только O (n log n) на средний. Худший случай - O (n ^ 2)! Сортировка слиянием всегда O (n log n). В случаях, когда производительность или скорость реагирования в реальном времени являются обязательными и ваши входные данные могут поступать из злонамеренного источника, вам не следует использовать обычную быструю сортировку.

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

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

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

Му! Quicksort не лучше, чем mergesort, он больше подходит для других приложений.

Mergesort is worth considering if speed is of the essence, bad worst-case performance cannot be tolerated, and extra space is available.1

Вы заявили, что они «Они оба О (нлогн) […]». Это не правильно. «В худшем случае Quicksort использует примерно n ^ 2/2 сравнений». 1.

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

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

Сортировку слиянием можно реализовать на месте, так что не требуется дополнительное пространство. Например, с двусвязным списком: stackoverflow.com/questions/2938495/…

lanoxx 08.05.2013 11:51
Ответ принят как подходящий

Quicksort имеет время выполнения для худшего случая O (п2) и среднее время выполнения O (пlogп). Однако сортировка слиянием лучше во многих сценариях, потому что многие факторы влияют на время выполнения алгоритма, и, если взять их все вместе, быстрая сортировка побеждает.

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

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

На практике многие современные реализации быстрой сортировки (в частности, std::sort из libstdC++) на самом деле являются интросорт, теоретический худший случай которого равен O (пlogп), как и сортировка слиянием. Это достигается за счет ограничения глубины рекурсии и переключения на другой алгоритм (heapsort), когда он превышает logп.

В статье в Википедии говорится, что он переключается на heapsort, а не mergesort ... просто к сведению.

Sev 09.09.2010 10:46

@Sev:… как и в оригинальной статье. Спасибо, что указали на ошибку. - Не то чтобы это действительно важно, поскольку их асимптотическое время работы одинаково.

Konrad Rudolph 11.09.2010 15:46

почему этот ответ выбран как правильный? Все, что он объясняет, - это то, как быстро устранять проблемы. Он до сих пор не объясняет, почему быстрая сортировка используется чаще, чем другие? Ответ: «Быстрая сортировка используется чаще, чем другая, потому что после одной глубины вы можете переключиться на heapsort»? .. почему бы тогда не использовать heapsort в первую очередь? .. просто пытаюсь понять ...

codeObserver 04.04.2011 11:13

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

Konrad Rudolph 04.04.2011 11:17

@KonradRudolph Что вы имеете в виду, когда говорите, что "быстрая сортировка быстрее, чем сортировка слиянием"? Вы говорите о теоретическом анализе или о реализации на практике? Насколько я понимаю, сортировка слиянием лучше с точки зрения анализа, но из-за кеширования быстрая сортировка часто предпочтительнее (без учета всех других факторов).

user1520427 27.10.2012 05:36

@ user1520427 Я говорил о производительности на практике. Я не проводил тщательного анализа (то есть не только с точки зрения большого О) количества сравнений, необходимых при сортировке слиянием, - я подозреваю, что оно может быть даже меньше, чем в среднем при быстрой сортировке.

Konrad Rudolph 27.10.2012 11:43

Quicksort также лучше с точки зрения памяти.

Shashwat 19.05.2014 01:38

@Shashwat Это действительно так по сравнению с сортировкой слиянием, но мой ответ применяется в более общем плане по сравнению с другими методами сортировки, и там аспект памяти больше не соответствует действительности. Например, heapsort использует меньше памяти, чем quicksort (O (1) vs O (log n)).

Konrad Rudolph 19.05.2014 10:55

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

Jason 29.01.2019 18:45

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

Julian 08.03.2021 18:55

@Julian Настоящий статистический анализ немного сложнее, и я забыл подробности, но, если у вас нет хорошей ссылки, я не верю, что медиана из трех лучше, чем случайная точка поворота (вероятность получения супер-O (n log n ) время выполнения можно доказать экспоненциально низким). Фактически, на практике случайный поворот выполняет отлично. Его главный недостаток (и почему он редко используется в стандартных библиотеках) заключается в том, что он изменяет глобальное случайное состояние. Да, эффективность ГСЧ - это проблема, но есть очень эффективные ГПСЧ.

Konrad Rudolph 08.03.2021 19:00

Вот ваша ссылка: citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.8162. Честно говоря, я считаю очевидным, что медиана трех элементов с большей вероятностью будет ближе к середине диапазона, чем любой отдельный элемент.

Julian 08.03.2021 19:13

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

Konrad Rudolph 08.03.2021 19:21

Это так, см. «Выбор элемента разделения» на странице 1254. И данные рандомизируются для начала (в противном случае не было бы необходимости сортировать их), поэтому выбираете ли вы первые три элемента, три случайных элемента или первый-средний -в последнем случае вы получите такое же социастическое поведение. Однако первый-средний-последний лучше справляется с данными, которые уже несколько отсортированы или отсортированы обратным образом.

Julian 08.03.2021 19:27

@ Джулиан «несортированный» ≠ «случайный». Раздел, который вы цитируете, не сравнивает время выполнения, он сравнивает количество сравнений. Очевидно, что здесь мы можем добиться большего успеха, чем случайный - простой выбор истинной медианы каждый раз дает лучший результат, но это непомерно дорого (как объясняется в том же разделе). Вычисление медианы трех случайных элементов - в общем случае - абсолютно не обладает такими же стохастическими свойствами, как выбор трех фиксированных элементов (хотя я признаю, что на практике разница очень незначительна).

Konrad Rudolph 08.03.2021 19:38

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

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

Во втором предложении говорится: «... сортировка слиянием потенциально намного медленнее, чем ... сортировка слиянием». Предположительно, первая ссылка должна быть на быструю сортировку.

Jonathan Leffler 28.09.2008 06:26

Сортировка слиянием стабильна только в том случае, если алгоритм слияния стабилен; это не гарантируется.

Clearer 21.12.2014 22:05

@Clearer Если для сравнения используется <=, а не <, то это гарантировано, и нет причин не делать этого.

Jim Balter 03.11.2018 15:49

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

Clearer 04.11.2018 19:46

@Clearer quicksort - это не сортировка слиянием ... ваше утверждение от 21 декабря 2014 года, на которое я ответил, было строго о сортировке слиянием и о том, стабильна ли она. быстрая сортировка и то, что быстрее, совершенно не имеет отношения к вашему комментарию или моему ответу. Конец обсуждения для меня ... снова и снова.

Jim Balter 04.11.2018 20:01

С помощью быстрой сортировки можно легко объединить два массива в один (скопировать массивы в массив и отсортировать его - готово). Это очень плохой способ слияния, но он показывает, что можно сделать алгоритм слияния нестабильным (или эффективным). Мой комментарий о том, почему быстрая сортировка может быть быстрее, чем сортировка слиянием, был нацелен на исходный ответ.

Clearer 04.11.2018 23:52

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

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

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

На самом деле, если вы говорите о библиотечной функции qsort (), она может быть реализована или не реализована как быстрая сортировка.

Thomas Padron-McCarthy 12.10.2008 11:03

Конрад, извините за то, что я немного задолбался, но где вы найдете эту гарантию? Я не могу найти его в стандарте ISO C или в стандарте C++.

Thomas Padron-McCarthy 21.10.2008 15:12

qsort в GNU libc - это сортировка слиянием, за исключением случаев, когда количество элементов действительно гигантское или временная память не может быть выделена. cvs.savannah.gnu.org/viewvc/libc/stdlib/…

Jason Orendorff 10.12.2009 03:49

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

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

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

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

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

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

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

Очень хорошо, не задумывался о предположениях, сделанных для доступа к структуре данных. Хорошее понимание :)

chutsu 20.02.2014 16:12

Можете ли вы объяснить, что вы подразумеваете под «поиском на диск», означает ли это поиск какого-то единственного значения, когда данные хранятся на диске?

James Wierzba 19.06.2015 20:11

@JamesWierzba Я понял из контекста, что он означает «поиск места на диске». «Поиск» на вращающемся дисковом устройстве означает захват считывающей головки и перемещение ее на новый абсолютный адрес, что является заведомо медленной операцией. Когда вы обращаетесь к данным в том порядке, в котором они были сохранены, аппаратному обеспечению диска не нужно искать, оно просто перемещается с высокой скоростью, последовательно считывая элементы.

nclark 27.05.2016 19:06

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

SmallChess 18.09.2016 13:15

Кто-нибудь может объяснить это поподробнее? Вот как я это вижу: Быстрая сортировка: если мы идем со случайным поворотом, стек вызовов имеет фрагменты массива, разделенные случайным образом. Для этого требуется произвольный доступ. Однако для каждого вызова в стеке левый и правый указатели перемещаются последовательно. Я предполагаю, что они будут храниться в кеше. Свопы - это снова операции с информацией, которая находится в кеше (и в конечном итоге записывается на диск). (продолжение в моем следующем комментарии)

sam 06.05.2017 05:13

MergeSort: стек вызовов строится путем логарифмического деления массива в глубину. И, слияние снизу (самая левая часть массива) вверх. Разделить часть массива можно только с помощью индексов. Таким образом, нет необходимости случайным образом перемещаться по массиву. Однако при слиянии дополнительный / результирующий массив будет построен / выгружен при последовательной записи. Это правильно?

sam 06.05.2017 05:13

Просто вклад избегая накладных расходов на чтение / запись диска дорогостоящий: при сортировке очень больших данных, требующих доступа к диску, выгодно переключать направление сортировки для каждого прохода. То есть на самом верхнем уровне петли, когда вы переходите от 0 к n и в следующий раз, когда вы переходите от n к 0. Это дает преимущество отступления (сортировки) блоков данных, которые уже доступны в памяти (кэше), и двойной атаки только для одного доступа к диску. Я думаю, что большинство СУБД используют этот метод оптимизации.

ssd 02.03.2018 18:20

@anujpradhan "Это то, чему книга не может научить" - О, правда? Это закон физики? Потому что я узнал это из книг.

Jim Balter 03.11.2018 16:04

Кто-нибудь доработает термин «падение индексов»?

Midhunraj R Pillai 26.12.2020 10:32

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

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

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

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

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

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

Konrad Rudolph 20.10.2008 13:14

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

Anders Eurenius 20.10.2008 19:31

Обратите внимание, что если вы используете GNU libc, qsort является сортировкой слиянием.

Jason Orendorff 10.12.2009 03:44

Эээ, если быть точным, это сортировка слиянием, если не может быть выделена необходимая временная память. cvs.savannah.gnu.org/viewvc/libc/stdlib/…

Jason Orendorff 10.12.2009 03:52

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

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

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

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

Nick Gallimore 24.02.2019 22:12

Трудно сказать. Худший из MergeSort - это 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 бесконечно. это как если бы наихудший вариант MergeSort лучше, чем лучший вариант QuickSort, почему мы используем быструю сортировку? Но помните, что MergeSort не на месте, для этого требуется 2n мемройского пространства. не включайте в анализ алгоритма. Одним словом, MergeSort действительно быстрее, чем быстрая сортировка, но на самом деле вам нужно учитывать пространство памяти, стоимость копирования массива, слияние происходит медленнее, чем быстрая сортировка. Я однажды сделал эксперимент, в котором мне дали 1000000 цифр в java классом Random, и потребовалось 2610 мс при сортировке слиянием, 1370 мс при быстрой сортировке.

Ответ был бы слегка склонен к быстрой сортировке по отношению к изменениям, внесенным с помощью DualPivotQuickSort для примитивных значений. Он используется в JAVA 7 для сортировки в java.util.Arrays

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

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

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

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

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

[5, 4, 3, 2, 1]

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

O (2n) == O (n). Правильное утверждение состоит в том, что Mergesort требует O (n) дополнительной памяти (точнее, ей требуется n / 2 вспомогательной памяти). А это не относится к связанным спискам.

Jim Balter 03.11.2018 15:54

@JimBalter Сэр, не могли бы вы поделиться с нами своими блестящими и стоящими идеями об их выступлениях в качестве ответа на вопрос? Заранее спасибо.

snr 28.11.2018 13:51

Чем хорош Quicksort?

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

Всегда ли Quicksort лучше Mergesort?

Не совсем.

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

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

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

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

Jim Balter 03.11.2018 15:56

Небольшие дополнения к сортировкам быстрого и слияния.

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

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

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

Общий алгоритм сортировки слиянием:

  1. Сортировать левый подмассив
  2. Сортировать правый подмассив
  3. Объедините 2 отсортированных подмассива

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

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

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

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

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

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

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

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

Потом Н-7 ... Потом Н-15 ... Потом Н-32 ...

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

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

То, что повороты не являются частью сортировок на следующем уровне, не является причиной большей производительности QS. См. Другие ответы для получения дополнительной информации.

Jim Balter 03.11.2018 15:37

@JimBalter Какие «другие ответы» вы имеете в виду? Верхний ответ просто говорит, что QS «требует немного дополнительного места и демонстрирует хорошую локальность кеша», но не дает никаких объяснений, почему это так, и не дает никаких ссылок. Второй ответ просто говорит, что сортировка слиянием лучше для больших наборов данных.

RvPr 13.06.2019 23:01

Вы перемещаете столбы ворот от того, почему QS более эффективен, к объяснению основных фактов о том, как он работает. Для этого нужны ответы на другие вопросы: stackoverflow.com/questions/9444714/… ... Надеюсь, вам хватит; Я не буду отвечать дальше.

Jim Balter 14.06.2019 04:30

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

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

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

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

Обновлено: я только что обнаружил, что худший / лучший / средний случай сортировки слиянием всегда 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. Он не сильно выигрывает от уже отсортированного массива с точки зрения сравнений (однако он делает это с точки зрения свопов и, вероятно, с точки зрения прогнозов ветвлений внутри ЦП).

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

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

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

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

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

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

Превосходит ли qsort сортировку слиянием даже для отсортированных входных данных, если сравнение дешево?

eonil 17.05.2019 23:07

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

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

Jim Balter 03.11.2018 15:30

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

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

Jim Balter 03.11.2018 15:32

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

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

Быстрая сортировка похожа на управленческий подход, когда человек изначально не заботится о каком-либо порядке, а только о соблюдении широкого критерия без учета порядка. Затем перегородки сужаются, пока не получится отсортированный набор. Настоящая проблема в Quicksort - найти раздел или критерий в темноте, когда вы ничего не знаете об элементах для сортировки. Вот почему нам нужно либо приложить некоторые усилия, чтобы найти среднее значение, либо выбрать 1 случайным образом, либо какой-то произвольный «управленческий» подход. Поиск идеальной медианы может потребовать значительных усилий и снова привести к глупому подходу снизу вверх. Таким образом, Quicksort говорит, что просто выберите случайную точку поворота и надейтесь, что она будет где-то посередине, или поработайте, чтобы найти медианное значение 3, 5 или что-то большее, чтобы найти лучшую медиану, но не планируйте быть идеальным и не теряйте в любое время при первоначальном заказе. Кажется, это хорошо, если вам повезет, или иногда снижается до n ^ 2, когда вы не получаете медиану, а просто рискуете. В любом случае данные случайны. верно. Так что я больше согласен с логическим подходом быстрой сортировки сверху -> вниз, и оказывается, что шанс, который он берет в отношении выбора и сравнения поворотных точек, которые он сохраняет ранее, кажется, работает лучше в большем количестве раз, чем любой дотошный и тщательный стабильный подход снизу -> вверх, например Сортировка слиянием. Но

Quicksort извлекает выгоду из случайности выбора опорных точек. Случайный поворот, естественно, будет иметь тенденцию к разделению 50:50 и вряд ли будет постоянно двигаться к одной из крайностей. Постоянный коэффициент nlogn довольно низок до тех пор, пока среднее разбиение не будет составлять 60-40 или даже до 70-30.

Winter Melon 02.01.2018 01:05

Это полная чушь. quicksort используется из-за своей производительности, а не «философии» ... и утверждения о том, что «порядок неизбежно будет потерян», просто ложны.

Jim Balter 03.11.2018 15:26

Учитывайте как временную, так и пространственную сложность. Для сортировки слиянием: Временная сложность: O (nlogn), Сложность пространства: O (nlogn)

Для быстрой сортировки: Сложность времени: O (n ^ 2), Сложность пространства: O (n)

Теперь они оба выигрывают в одном сценарии каждый. Но, используя случайный поворот, вы почти всегда можете уменьшить временную сложность быстрой сортировки до O (nlogn).

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

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

1- Вспомогательное пространство: Быстрая сортировка - это алгоритм сортировки на месте. Сортировка на месте означает, что для выполнения сортировки не требуется дополнительное пространство для хранения. Сортировка слиянием, с другой стороны, требует временного массива для слияния отсортированных массивов и, следовательно, его нет на месте.

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

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

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

Другие вопросы по теме