Cортировка слиянием с использованием O(1) дополнительной памяти — различия между версиями
Whiplash (обсуждение | вклад) |
Whiplash (обсуждение | вклад) |
||
Строка 5: | Строка 5: | ||
− | Разобьем наш массив на | + | Разобьем наш массив на <tex>cnt</tex> подряд идущих блоков длиной <tex>len = \lfloor \sqrt{n} \rfloor </tex>. Остаток трогать не будем. |
[[Файл:Merge_O(1)_2.png|center|525px]] | [[Файл:Merge_O(1)_2.png|center|525px]] | ||
Строка 15: | Строка 15: | ||
− | Отсортируем блоки по возрастанию по первому элементу (если первые элементы равны, тогда по последнему). Для этого подойдет любая квадратичная или более быстрая сортировка, которая требует | + | Отсортируем блоки по возрастанию по первому элементу (если первые элементы равны, тогда по последнему). Для этого подойдет любая квадратичная или более быстрая сортировка, которая требует <tex> O (1) </tex> дополнительной памяти. Здесь нам выгодно использовать алгоритм, линейный по числу обменов, т.е. подходит [[Сортировка выбором|сортировка выбором (selection sort)]]. |
Так как блоков <tex> \sqrt{n} </tex>, то количество операций на этом шаге <tex> O(n) </tex>. | Так как блоков <tex> \sqrt{n} </tex>, то количество операций на этом шаге <tex> O(n) </tex>. | ||
Строка 21: | Строка 21: | ||
[[Файл:Merge_O(1)_4.png|center|525px]] | [[Файл:Merge_O(1)_4.png|center|525px]] | ||
+ | |||
+ | Пользуясь буфером обмена, последовательно сольем пары соседних блоков. В результате мы получим, что первые <tex>len \cdot (cnt - 1)</tex> элементов исходного массива отсортированы. | ||
+ | |||
+ | [[Файл:Merge_O(1)_5.png|center|525px]] | ||
+ | |||
+ | |||
+ | == Использование буфера обмена == | ||
+ | Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененный буфер. Результат начинаем записывать с начала первой группы. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину, и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно. | ||
+ | |||
+ | [[Файл:Merge_O(1)_buffer.png|center|355px]] | ||
=== Шаг 3 === | === Шаг 3 === |
Версия 01:49, 29 мая 2012
Содержание
Алгоритм слияния
На вход алгоритм получает массив, который состоит из двух отсортированных кусков:
Разобьем наш массив на подряд идущих блоков длиной . Остаток трогать не будем.
Найдем блок, содержащий конец первого отсортированного куска. Поменяем его с последним блоком. В дальнейшем будем использовать его как буфер обмена.
Отсортируем блоки по возрастанию по первому элементу (если первые элементы равны, тогда по последнему). Для этого подойдет любая квадратичная или более быстрая сортировка, которая требует дополнительной памяти. Здесь нам выгодно использовать алгоритм, линейный по числу обменов, т.е. подходит сортировка выбором (selection sort).
Так как блоков
, то количество операций на этом шаге .
Пользуясь буфером обмена, последовательно сольем пары соседних блоков. В результате мы получим, что первые элементов исходного массива отсортированы.
Использование буфера обмена
Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененный буфер. Результат начинаем записывать с начала первой группы. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину, и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно.
Шаг 3
Попытаемся слить первую и вторую группу. Поменяем местами первую группу и часть остатка. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененную часть остатка. Результат начинаем записывать с начала первой группы. Чтобы ничего не перезаписалось, вместо записи используем обмен элементов. Так как группы имеют одинаковую длину, и между указателем на вторую группу и указателем на запись расстояние равно длине группы, то слияние произойдет корректно.
Пример : Пусть длины групп равны трем и
, где первая группа , а втораяНомер операции | Массив до выполнения операции | Массив после выполнения операции |
---|---|---|
1 | ||
2 | ||
3 | ||
4 | ||
5 | ||
6 |
Потом аналогично сольем вторую и третью группу и так до последней группы. Так как после второго шага количество инверсий для каждого элемента не больше
, то ему надо сдвинуться влево не больше, чем на элементов, поэтому в конце, не учитывая остаток, массив будет отсортированный.Количество групп
, и каждое слияние работает за , поэтому количество операций на этом шаге .Шаг 4
Пусть размер остатка
. Начиная с конца, разобьем наш массив на подряд идущие группы длиной s. Используя квадратичную или более быструю сортировку, которая требует дополнительной памяти , отсортируем подмассив длиной , который находится в конце. На последних местах будут находиться s максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен . По аналогии с шагом 3 в обратном порядке сливаем группы длиной .Количество операций на этом шаге
.Шаг 5
Опять, используя экономную по памяти, хотя и квадратичную, сортировку, отсортируем:
- остаток и первую группу.
- последнюю группу.
Не стоит забывать, что после новой разметки остаток находится в начале, а не в конце.
В результате массив будет отсортированным
Количество операций на этом шаге
.