Cортировка слиянием с использованием O(1) дополнительной памяти
После того, как мы научились сливать с использованием дополнительной памяти и операций , оставшееся решение задачи становится очевидным. Будем сливать не рекурсивно, чтобы сэкономить память в стеке. Пусть на i-том шаге у нас отсортированы подряд идущие, не пересекающиеся куски массива размером (за исключением последнего, его размер может быть меньше). Тогда сольем сначала первый и второй кусок, потом третий и четвертый и так до последнего. Если последнему куску нету пары, то просто ничего не делаем. Выполняем это для всех i от до
Содержание
Алгоритм слияние
На вход алгоритм получает массив, который состоит из двух отсортированных кусков. На выходе алгоритм возвращает отсортированный массив. Алгоритм состоит из нескольких шагов.
Шаг 1
Разобьем наш массив на группы подряд идущих элементов длиной . Остаток трогать не будем. Найдем группу, содержащую конец первого отсортированного куска. Поменяем ее с последней. Добавим последнюю группу в остаток.
Количество операций на этом шаге .
Шаг 2
Возьмем и отсортируем группы по первому элементу (в случае равенства по последнему). Это можно сделать любой квадратичной или более быстрой сортировкой, которая требует дополнительной памяти , например сортировка выбором. Следует заметить, что, после сортировки этих групп, элементы, которые стоят левее заданного и больше его, находились в противоположном куске отсортированного массива, также они находятся в пределах одной группы, поэтому количество инверсий для каждого элемента не больше .
Так как кусков , количество операций на этом шаге .
Шаг 3
Попытаемся слить первую и вторую группу. Поменяем местами первую группу и часть остатка. И, как в обычном слиянии, пользуясь двумя указателями, сливаем вторую группу и только что измененную часть остатка. Результат начинаем записывать с начала первой группы. Чтобы ничего не перезаписалось, вместо записи используем обмен элементов. Так как группы имеют одинаковую длину, и между указателем на вторую группу и указателем на запись расстояние равно длине группы, то слияние произойдет корректно.
Пример : Пусть длины групп равны трем и , где первая группа , а вторая
| Номер операции | Массив до выполнения операции | Массив после выполнения операции |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 | ||
| 4 | ||
| 5 | ||
| 6 |
Потом аналогично сольем вторую и третью группу и так до последней группы. Так как после второго шага количество инверсий для каждого элемента не больше , то ему надо сдвинутся влево не больше, чем на элементов, поэтому в конце, не учитывая остаток, массив будет отсортированный.
Количество групп , и каждое слияние работает за , поэтому количество операций на этом шаге .
Шаг 4
Пусть размер остатка . Начиная с конца, разобьем наш массив на подряд идущие группы длиной s. Используя квадратичную или более быструю сортировку, которая требует дополнительной памяти , отсортируем подмассив длиной , который находится в конце. На последних местах будут находится s максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен. По аналогии с шагом 3 в обратном порядке сливаем группы длиной .
Количество операций на этом шаге .
Шаг 5
Опять, используя экономную по памяти, хотя и квадратичную, сортировку, отсортируем:
- остаток и первую группу.
- последнюю группу.
Не стоит забывать, что после новой разметки остаток находится в начале, а не в конце.
В результате массив будет отсортированным
Количество операций на этом шаге .