Сортировка слиянием
характеристики | значения |
---|---|
сложность всегда | |
дополнительная память | или 1 |
стабильная ли? | ✅ |
Merge sort - рекурсивный алгоритм сортировки, разбивающий основной массив на подмассивы, сортирующий их и собирающий всё обратно в отсортированном виде. Такие алгоритмы называются "разделяй и властвуй", но о таких алгоритмах попозже. (в курсе такого не будет=) )
Алгоритм
Алгоритм Merge sort
на подмасиве выглядит следующем образом:
- Если
l = r
, ничего не делать, так как массив отсортирован. - Вычислить середину отрезка: с округлением вниз.
- Рекурсивно отсортировать .
- Рекурсивно отсортировать .
- Объединить (
merge
) два уже отсортированных массива и в один общий отсортированный массив
Объединение отсортированных массивов
Пункт (5) является очень простым. Используя метод двух указателей, можно двигать границы и добавляя в итоговый массив самый маленький из и .
Преимущества алгоритма
- Стабильный алгоритм
- Сложность алгоритма в худшем случае — , следовательно, он идеален для больших массивов.
- Алгоритм параллелен — его можно спокойно ускорить, разбив действия на разные потоки процессора.
Недостатки алгоритма
- Требователен к памяти.
- Не всегда оптимален для маленьких массивов.
Реализация алгоритма
Я специально оставлю реализацию на подумать (я проверил на задаче, она работает). (реализация взята отсюда)
void mergeSort(int* a, int* tmp, int l, int r) {
if (l == r) return; // (1)
int m = l + (r - l) / 2; // (2)
mergeSort(a, tmp, l, m); // (3) рекурсивный запуск для левой части
mergeSort(a, tmp, m + 1, r); // (4) рекурсивный запуск для правой части
for (int i = l; i <= r; i++)
tmp[i] = a[i]; // массив tmp равен а (соответствующие подотрезки)
// (5) два указателя, очень красивые
int ai = l, bi = m + 1;
for (int i = l; i <= r; i++) {
if (bi > r || ai <= m && tmp[ai] <= tmp[bi])
a[i] = tmp[ai++];
else
a[i] = tmp[bi++];
}
}
Запуск функции сортировки
int *tmp = new int[size]; mergeSort(arr, tmp, 0, size-1);
.
Можно написать без использование глобального массива tmp
.
Доказательство сложности алгоритма
Чтобы оценить время работы этого алгоритма, составим рекуррентное соотношение. Это на любом сайте написано. Думайте.
Давайте объясню на пальцах :
Во время разбиения массива на две части, каждый элемент массива обрабатывается один раз. Всего уровней рекурсии будет , так как каждый раз массив делится пополам. При слиянии двух частей массива для всего уровня выполняется операций.
Ввод
49 12 -3 15
Работа алгоритма
- Разбиение массива { 49 12 -3 15 } на два подмассива { 49 12 } и { -3 15 }
- Разбиение подмассивов на части { 49 } и { 12 }, { -3 } и { 15 }
- Сборка отсортированных подмассивов: { 12 49 } и { -3 15 }
- Сборка массива из подмассивов: { -3 12 15 49 }
Вывод
-3 12 15 49
Можно решить задачу о количестве инверсий в массиве. TODO?