Сортировка слиянием
Merge sort делит диапазон пополам, сортирует обе половины и за линейное время сливает их. Используем полуинтервалы [from, to).
static void mergeSort(int[] values) {
int[] buffer = new int[values.length];
mergeSort(values, buffer, 0, values.length);
}
private static void mergeSort(
int[] values, int[] buffer, int from, int to
) {
if (to - from < 2) return;
int middle = from + (to - from) / 2;
mergeSort(values, buffer, from, middle);
mergeSort(values, buffer, middle, to);
merge(values, buffer, from, middle, to);
}
private static void merge(
int[] values, int[] buffer, int from, int middle, int to
) {
int left = from;
int right = middle;
int out = from;
while (left < middle && right < to) {
if (values[left] <= values[right]) {
buffer[out++] = values[left++];
} else {
buffer[out++] = values[right++];
}
}
while (left < middle) buffer[out++] = values[left++];
while (right < to) buffer[out++] = values[right++];
System.arraycopy(buffer, from, values, from, to - from);
}Инвариант слияния
Перед каждой итерацией buffer[from, out) содержит наименьшие уже рассмотренные элементы двух половин в отсортированном порядке. Следующим берется меньший из голов оставшихся диапазонов.
<= выбирает равный элемент из левой половины первым и сохраняет стабильность.
Сложность
- уровней деления:
log n; - работа слияния на каждом уровне:
O(n); - время во всех случаях:
O(n log n); - дополнительный массив:
O(n); - стек рекурсии:
O(log n).
Главное преимущество — гарантированное время и стабильность. Цена для массивов — буфер и копирование. Для linked list слияние можно делать перестановкой ссылок.
См. Быстрая сортировка против сортировки слиянием, Merge sorted array.
На собеседовании
- Почему
O(n log n)?log nуровней поO(n)слияния. - Почему алгоритм стабилен? При равенстве первым выбирается элемент левой половины.
- Что является базовым случаем? Диапазон длины меньше двух.
- Дополнительная память массива?
O(n). - Можно ли переиспользовать один buffer? Да, как в примере.