Я знаю, что вопрос не слишком конкретен.
Все, что я хочу, это чтобы кто-то сказал мне, как преобразовать обычную сортировку слиянием в сортировку слиянием на месте (или сортировку слиянием с постоянными дополнительными пробелами).
Все, что я могу найти (в сети), это страницы с надписью «это слишком сложно» или «выходит за рамки этого текста».
Единственные известные способы слияния на месте (без какого-либо дополнительного пространства) слишком сложны, чтобы сводить их к практической программе. (взято отсюда )
Даже если это слишком сложно, какова основная концепция того, как сделать сортировку слиянием на месте?
Ответы:
Кнут оставил это как упражнение (Том 3, 5.2.5). Существуют слияния на месте. Они должны быть выполнены тщательно.
Во-первых, наивное слияние на месте, как описано здесь , не является правильным решением. Это снижает производительность до O (N 2 ) .
Идея состоит в том, чтобы отсортировать часть массива, используя остальное в качестве рабочей области для объединения.
Например, как следующая функция слияния.
Он принимает массив
xs
, два отсортированных подмассива представлены в виде диапазонов[i, m)
и[j, n)
соответственно. Рабочая зона начинается сw
. Сравните со стандартным алгоритмом слияния, приведенным в большинстве учебников, он обменивается содержимым между отсортированным подмассивом и рабочей областью. В результате предыдущая рабочая область содержит объединенные отсортированные элементы, в то время как предыдущие элементы, хранящиеся в рабочей области, перемещаются в два вложенных массива.Однако есть два ограничения, которые должны быть выполнены:
С этим определенным алгоритмом объединения легко представить решение, которое может сортировать половину массива; Следующий вопрос заключается в том, как поступить с остатком несортированной детали, хранящейся в рабочей области, как показано ниже:
Одна интуитивная идея состоит в рекурсивной сортировке другой половины рабочей области, таким образом, только 1/4 элемента еще не отсортированы.
Ключевым моментом на этом этапе является то, что мы должны рано или поздно объединить отсортированные 1/4 элемента B с отсортированными 1/2 элементами A.
Осталась ли рабочая область, которая содержит только 1/4 элемента, достаточно большой, чтобы объединить А и В? К сожалению, это не так.
Однако второе ограничение, упомянутое выше, дает нам подсказку, что мы можем использовать его, расположив рабочую область так, чтобы она перекрывалась с любым подмассивом, если мы сможем обеспечить последовательность слияния, чтобы не слитые элементы не были перезаписаны.
На самом деле, вместо сортировки второй половины рабочей области, мы можем отсортировать первую половину и поместить рабочую область между двумя отсортированными массивами следующим образом:
Эта установка эффективно организует перекрытие рабочей области с подмассивом A. Эта идея предложена в [Jyrki Katajainen, Tomi Pasanen, Jukka Teuhola. `` Практическая сортировка на месте ''. Nordic Journal of Computing, 1996].
Таким образом, остается только повторить вышеуказанный шаг, который уменьшает рабочую область с 1/2, 1/4, 1/8,… Когда рабочая область становится достаточно маленькой (например, осталось только два элемента), мы можем переключитесь на тривиальную сортировку вставки, чтобы завершить этот алгоритм.
Вот реализация в ANSI C, основанная на этом документе.
Где wmerge определен ранее.
Полный исходный код можно найти здесь, а подробное объяснение можно найти здесь
Кстати, эта версия не самая быстрая сортировка слиянием, потому что она требует больше операций подкачки. Согласно моему тесту, это быстрее, чем стандартная версия, которая выделяет дополнительные пробелы в каждой рекурсии. Но это медленнее, чем оптимизированная версия, которая заранее удваивает исходный массив и использует его для дальнейшего слияния.
источник
Knuth left this as an exercise (Vol 3, 5.2.5).
относится к отл. 13. [40] Реализуйте метод внутренней сортировки, предложенный [в конце этого раздела], который производит сортировку случайных данных в O (N) единицах времени без только O (sqrt (N)) дополнительных областей памяти. ? ( 40 указывает на довольно сложную или длительную проблему, которая, возможно, подходит в качестве термина проекта в ситуациях в классе. )Включая свой «большой результат», эта статья описывает пару вариантов сортировки слиянием на месте (PDF):
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf
Сортировка на месте с меньшим количеством ходов
Юрки Катаяйнен, Томи А. Пасанен
Я думаю, что это тоже актуально. У меня есть распечатка, переданная мне коллегой, но я ее не читал. Кажется, что он охватывает базовую теорию, но я недостаточно знаком с темой, чтобы судить о том, насколько полно:
http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681
Оптимальное стабильное слияние
Антониос Симвонис
источник
Это на самом деле не просто и не эффективно, и я предлагаю вам не делать этого, если вам действительно не нужно (и, вероятно, вам не нужно делать это, если это не домашняя работа, поскольку приложения слияния на месте в основном теоретические). Вы не можете использовать быструю сортировку вместо этого? Быстрая сортировка будет быстрее в любом случае с несколькими более простыми оптимизациями, и ее дополнительная память будет O (log N) .
Во всяком случае, если вы должны сделать это, то вы должны. Вот что я нашел: один и два . Я не знаком с сортировкой слиянием на месте, но кажется, что основная идея состоит в том, чтобы использовать ротации для облегчения объединения двух массивов без использования дополнительной памяти.
Обратите внимание, что это медленнее, чем классическая сортировка слиянием, которой нет на месте.
источник
Критическим шагом является установление самого слияния . Это не так сложно, как видно из этих источников, но вы что-то теряете при попытке.
Глядя на один шаг слияния:
Мы знаем , что отсортированная последовательность меньше , чем все остальное, что х меньше , чем все остальное в A , и что у меньше , чем все остальное в B . В случае, когда x меньше или равен y , вы просто перемещаете указатель на начало A на единицу. В случае, когда у меньше х , вы должны перетасовать у всей А для сортировки . Этот последний шаг - то, что делает это дорогим (кроме в вырожденных случаях).
Как правило, дешевле (особенно когда массивы на самом деле содержат только отдельные слова на элемент, например, указатель на строку или структуру), чтобы обменять некоторое пространство на время и иметь отдельный временный массив, который вы сортируете назад и вперед.
источник
Просто для справки, вот хорошая реализация устойчивой сортировки слиянием на месте . Сложно, но не так уж плохо.
В итоге я реализовал как стабильную сортировку слиянием на месте, так и стабильную быструю сортировку на месте в Java. Обратите внимание, что сложность O (n (log n) ^ 2)
источник
Пример слияния без буфера в C.
Пример адаптивного слияния (оптимизирован).
Добавляет код поддержки и модификации для ускорения слияния, когда доступен вспомогательный буфер любого размера (все еще работает без дополнительной памяти). Используются прямое и обратное объединение, вращение кольца, объединение и сортировка небольших последовательностей и итеративное объединение.
источник
Этот ответ имеет пример кода , который реализует алгоритм, описанный в статье Практическое слияние на месте Бинг-Чао Хуанга и Майкла А. Лэнгстона. Я должен признать, что я не понимаю деталей, но данная сложность шага слияния равна O (n).
С практической точки зрения есть свидетельства того, что чистые реализации на месте не работают лучше в реальных сценариях. Например, стандарт C ++ определяет std :: inplace_merge , так как имя подразумевает операцию слияния на месте.
Предполагая, что библиотеки C ++ обычно очень хорошо оптимизированы, интересно посмотреть, как это реализовано:
1) libstdc ++ (часть базы кода GCC): std :: inplace_merge
Реализация делегирует __inplace_merge , что устраняет проблему, пытаясь выделить временный буфер:
В противном случае он возвращается к реализации ( __merge_without_buffer ), которая не требует дополнительной памяти, но больше не выполняется за время O (n).
2) libc ++ (часть базы кода Clang): std :: inplace_merge
Выглядит похоже. Он делегирует функции , которая также пытается выделить буфер . В зависимости от того, получил ли он достаточно элементов, он выберет реализацию. Резервная функция с постоянной памятью называется __buffered_inplace_merge .
Может быть, даже запасной вариант - все еще время O (n), но дело в том, что они не используют реализацию, если доступна временная память.
Обратите внимание, что стандарт C ++ явно дает реализациям свободу выбора этого подхода, снижая требуемую сложность с O (n) до O (N log N):
Конечно, это нельзя считать доказательством того, что постоянное пространство на месте слияния за O (n) время никогда не должно использоваться. С другой стороны, если это будет быстрее, оптимизированные библиотеки C ++, вероятно, переключатся на реализацию такого типа.
источник
Это моя версия C:
источник
Существует относительно простая реализация сортировки слиянием на месте с использованием оригинальной методики Kronrod, но с более простой реализацией. Наглядный пример, иллюстрирующий эту технику, можно найти здесь: http://www.logiccoder.com/TheSortProblem/BestMergeInfo.htm .
Есть также ссылки на более подробный теоретический анализ того же автора, связанный с этой ссылкой.
источник
Я только что попробовал на месте алгоритм слияния для сортировки слиянием в JAVA , используя алгоритм сортировки вставкой, используя следующие шаги.
1) Два отсортированных массива доступны.
2) Сравните первые значения каждого массива; и поместите наименьшее значение в первый массив.
3) Поместите большее значение во второй массив, используя сортировку вставки (перемещение слева направо).
4) Затем снова сравните второе значение первого массива и первое значение второго массива и сделайте то же самое. Но когда происходит обмен, есть некоторая подсказка при пропуске, сравнивающая дальнейшие элементы, но требуется только обмен.
Я сделал некоторую оптимизацию здесь; держать меньшие сравнения в сортировке вставки.
Единственный недостаток, который я обнаружил в этом решении, заключается в том, что он требует большей замены элементов массива во втором массиве.
например)
First___Array: 3, 7, 8, 9
Второй массив: 1, 2, 4, 5
Затем 7, 8, 9 заставляет второй массив поменять (переместить влево на один) все его элементы по одному каждый раз, чтобы поместить себя в последний.
Таким образом, предположение о том, что обмен предметами здесь ничтожно по сравнению со сравнением двух предметов.
https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java
источник