Cортировка слиянием с использованием O(1) дополнительной памяти — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м
Строка 29: Строка 29:
  
  
== Использование буфера обмена ==
+
<tex>S</tex> - размер остатка. Используя квадратичную или более быструю сортировку, которая требует  <tex> O(1) </tex> дополнительной памяти, отсортируем подмассив длиной <tex> 2S </tex>, который находится в конце.
Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененный буфер. Результат начинаем записывать с начала первой группы. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину, и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно.
 
  
[[Файл:Merge_O(1)_buffer.png|center|355px]]
+
[[Файл:Merge_O(1)_6.png|center|525px]]
  
  
=== Шаг 4 ===
+
Теперь на последних <tex> S </tex> местах будут находиться <tex> S </tex> максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен <tex> S </tex>. По аналогии с тем что делали раньше отсортируем оставшуюся часть, разделив ее на блоки длиной <tex>S</tex>, используя последние <tex>S</tex> как буфер обмена. Не забудем после отсортировать буфер обмена.
Пусть размер остатка <tex> s </tex>. Начиная с конца, разобьем наш массив на подряд идущие группы длиной s. Используя квадратичную или более быструю сортировку, которая требует дополнительной памяти <tex> O(1) </tex>, отсортируем подмассив длиной <tex> 2s </tex>, который находится в конце. На последних <tex> s </tex> местах будут находиться s максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен <tex> s </tex>. По аналогии с шагом 3 в обратном порядке сливаем группы длиной <tex> s </tex>.  
 
  
Количество операций на этом  шаге <tex> O(n) </tex>.
+
[[Файл:Merge_O(1)_7.png|center|525px]]
  
=== Шаг 5 ===
 
Опять, используя экономную по памяти, хотя и квадратичную, сортировку, отсортируем:
 
  
#остаток и первую группу.
+
В результате мы получили отсортированный исходный массив.
#последнюю группу.
 
  
Не стоит забывать, что после новой разметки остаток находится в начале, а не в конце.
+
== Использование буфера обмена ==
 +
Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененный буфер. Результат начинаем записывать с начала первой группы. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину, и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно.
  
В результате массив будет отсортированным
+
[[Файл:Merge_O(1)_buffer.png|center|355px]]
 
 
Количество операций на этом  шаге <tex> O(n) </tex>.
 
  
=Ссылки и литература=
+
== Ссылки и литература ==
 
*[http://e-maxx.ru/bookz/files/knuth_3.djvu Д.Е.Кнут - Искусство программирования (том 3) упр 18 к разделу 5.2.4]
 
*[http://e-maxx.ru/bookz/files/knuth_3.djvu Д.Е.Кнут - Искусство программирования (том 3) упр 18 к разделу 5.2.4]
 
*[http://pastebin.com/hN2SnEfP  Реализация алгоритма на JAVA]
 
*[http://pastebin.com/hN2SnEfP  Реализация алгоритма на JAVA]

Версия 02:17, 29 мая 2012

Алгоритм слияния

На вход алгоритм получает массив, который состоит из двух отсортированных кусков:

Merge O(1) 1.png


Разобьем наш массив на [math]cnt[/math] подряд идущих блоков длиной [math]len = \lfloor \sqrt{n} \rfloor [/math]. Остаток трогать не будем.

Merge O(1) 2.png


Найдем блок, содержащий конец первого отсортированного куска. Поменяем его с последним блоком. В дальнейшем будем использовать его как буфер обмена.

Merge O(1) 3.png


Отсортируем блоки по возрастанию по первому элементу (если первые элементы равны, тогда по последнему). Для этого подойдет любая квадратичная или более быстрая сортировка, которая требует [math] O (1) [/math] дополнительной памяти. Здесь нам выгодно использовать алгоритм, линейный по числу обменов, т.е. подходит сортировка выбором.

Так как блоков [math] \sqrt{n} [/math], то количество операций на этом шаге [math] O(n) [/math].

Merge O(1) 4.png


Пользуясь буфером обмена, последовательно сольем пары соседних блоков. В результате мы получим, что первые [math]len \cdot (cnt - 1)[/math] элементов исходного массива отсортированы.

Количество групп [math] \sqrt{n} [/math], и каждое слияние работает за [math] О O(\sqrt{n}) [/math] , поэтому количество операций на этом шаге [math] O(n) [/math].

Merge O(1) 5.png


[math]S[/math] - размер остатка. Используя квадратичную или более быструю сортировку, которая требует [math] O(1) [/math] дополнительной памяти, отсортируем подмассив длиной [math] 2S [/math], который находится в конце.

Merge O(1) 6.png


Теперь на последних [math] S [/math] местах будут находиться [math] S [/math] максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен [math] S [/math]. По аналогии с тем что делали раньше отсортируем оставшуюся часть, разделив ее на блоки длиной [math]S[/math], используя последние [math]S[/math] как буфер обмена. Не забудем после отсортировать буфер обмена.

Merge O(1) 7.png


В результате мы получили отсортированный исходный массив.

Использование буфера обмена

Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененный буфер. Результат начинаем записывать с начала первой группы. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину, и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно.

Merge O(1) buffer.png

Ссылки и литература