Сортировка слиянием — различия между версиями
Tiss93 (обсуждение | вклад) |
|||
| Строка 1: | Строка 1: | ||
==Описание== | ==Описание== | ||
[[Файл:Merge-sort1.gif|right|380px|thumb|Действие алгоритма.]] | [[Файл:Merge-sort1.gif|right|380px|thumb|Действие алгоритма.]] | ||
| − | '''Сортировка слиянием''' — алгоритм сортировки | + | '''Сортировка слиянием''' — алгоритм сортировки. Он был предложен Джоном фон Нейманом в 1945 году. |
| − | Это стабильный алгоритм | + | Это стабильный алгоритм, использующий <tex>O(n)</tex> дополнительной памяти и <tex>O(n</tex> <tex>\log(n))</tex> времени. |
==Принцип работы== | ==Принцип работы== | ||
| − | + | Этот алгоритм хороший пример использования принципа «разделяй и властвуй» — сначала задача разбивается на несколько подзадач меньшего размера. Затем эти задачи решаются с помощью рекурсивного вызова или непосредственно, если их размер достаточно мал. Наконец, их решения комбинируются, и получается решение исходной задачи. | |
| − | + | Для процедуры слияния требуется два отсортированных массива. Зная, что массив из одного элемента по определению отсортирован, мы можем разработать такой алгоритм: | |
| − | # | + | # Массив разбивается на половинки до тех пор, пока размер "половинки" не станет равным единице. |
| − | # | + | # Каждая из получившихся частей сортируется отдельно. Или же это просто одиночный элемент. |
| − | # | + | # "Сливаем" два упорядоченных массива в один. |
===Слияние двух массивов=== | ===Слияние двух массивов=== | ||
| − | Допустим, у нас есть два отсортированных массива А и B размерами <tex>N_a </tex> и <tex>N_b </tex> соответственно, и мы хотим объединить их элементы в один большой отсортированный массив C размером <tex>N_a + N_b </tex> . Для этого можно применить процедуру | + | Допустим, у нас есть два отсортированных массива А и B размерами <tex>N_a </tex> и <tex>N_b </tex> соответственно, и мы хотим объединить их элементы в один большой отсортированный массив C размером <tex>N_a + N_b </tex> . Для этого можно применить процедуру слияния. Эта процедура заключается в том, что мы сравниваем элементы массивов (начиная с начала) и меньший из них записываем в финальный. И затем, в массиве у которого оказался меньший элемент, переходим к следующему элементу и сравниваем теперь его. В конце, если один из массивов закончился, мы просто дописываем в финальный другой массив. После мы наш финальный массив записываем заместо двух исходных и получаем отсортированный участок. |
[[Файл:Mergearr.png|right|300px|thumb|Пример работы процедуры слияния.]] | [[Файл:Mergearr.png|right|300px|thumb|Пример работы процедуры слияния.]] | ||
| − | + | ||
Алгоритм слияния формально можно записать следующим образом: | Алгоритм слияния формально можно записать следующим образом: | ||
| Строка 38: | Строка 38: | ||
==Рекурсивный алгоритм== | ==Рекурсивный алгоритм== | ||
[[Файл:Merge sort1.png|300px|right|thumb|Пример работы рекурсивного алгоритма сортировки слиянием]] | [[Файл:Merge sort1.png|300px|right|thumb|Пример работы рекурсивного алгоритма сортировки слиянием]] | ||
| − | + | Функция сортирует участок массива от элемента с номером left до элемента с номером right: | |
| + | |||
| + | right и left — правая и левая граница массива, middle — середина. | ||
| + | Условие выхода — если массив стал состоять из 1 элемента. | ||
<pre> | <pre> | ||
| − | sort(array a, int left, int right) | + | sort(array a, int left, int right) |
middle = right / 2 | middle = right / 2 | ||
| − | if middle == right | + | if middle == right |
return | return | ||
sort(a, left, middle) | sort(a, left, middle) | ||
| Строка 51: | Строка 54: | ||
Пример работы алгоритма показан на рисунке: | Пример работы алгоритма показан на рисунке: | ||
| + | |||
| + | ==Восходящая сортировка слиянием== | ||
| + | [[Файл:mergenonrec.png|300px|right|thumb|Пример работы восходящей сортировки слиянием]] | ||
| + | Помимо рекурсивного алгоритма существует и альтернативный. | ||
| + | Пример работы алгоритма показан на рисунке: | ||
| + | |||
| + | # Выделим память размером с занимаемой памяти исходного массива. | ||
| + | # Попарно сравним элементы, записывая во временную память. | ||
| + | # Поменяем указатели временного и исходного массива. | ||
| + | # Выполним слияние "кусочков" размером два. | ||
| + | # Повторяем до тех пор, пока не сделаем единый кусок. | ||
| + | # Удаляем временный массив. | ||
| + | |||
| + | Процедуру слияния надо будет изменить, так, что-бы она записывала результат в результирующий массив (mas1) | ||
| + | <pre>sort(array * mas, int elementsAmount) | ||
| + | array * mas1 = new array[elementsAmount]; | ||
| + | for(int size = 1; size < elementsAmount; size *= 2) { | ||
| + | int start = 0; | ||
| + | for(; (start + size) < elementsAmount; start += size * 2) | ||
| + | merge(mas + start, mas + start, | ||
| + | mas + start + size, | ||
| + | mas + start + size + min(size,elementsAmount - start - size)); | ||
| + | for(; start < elementsAmount; ++start) | ||
| + | mas1[start] = mas[start]; | ||
| + | array * temp = mas1; | ||
| + | mas1 = mas; | ||
| + | mas = temp; | ||
| + | delete[] mas1; | ||
| + | </pre> | ||
==Время работы== | ==Время работы== | ||
| Строка 63: | Строка 95: | ||
==Ссылки== | ==Ссылки== | ||
*[http://ru.wikipedia.org/wiki/Mergesort Википедия — сортировка слиянием] | *[http://ru.wikipedia.org/wiki/Mergesort Википедия — сортировка слиянием] | ||
| − | |||
*[http://www.sorting-algorithms.com/merge-sort Сортировка слиянием, анимация и свойства (англ.)] | *[http://www.sorting-algorithms.com/merge-sort Сортировка слиянием, анимация и свойства (англ.)] | ||
*[http://ru.wikibooks.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8_%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5%D0%BC Примеры реализации на различных языках (Википедия)] | *[http://ru.wikibooks.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8_%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5%D0%BC Примеры реализации на различных языках (Википедия)] | ||
Версия 23:18, 20 мая 2012
Содержание
Описание
Сортировка слиянием — алгоритм сортировки. Он был предложен Джоном фон Нейманом в 1945 году.
Это стабильный алгоритм, использующий дополнительной памяти и времени.
Принцип работы
Этот алгоритм хороший пример использования принципа «разделяй и властвуй» — сначала задача разбивается на несколько подзадач меньшего размера. Затем эти задачи решаются с помощью рекурсивного вызова или непосредственно, если их размер достаточно мал. Наконец, их решения комбинируются, и получается решение исходной задачи.
Для процедуры слияния требуется два отсортированных массива. Зная, что массив из одного элемента по определению отсортирован, мы можем разработать такой алгоритм:
- Массив разбивается на половинки до тех пор, пока размер "половинки" не станет равным единице.
- Каждая из получившихся частей сортируется отдельно. Или же это просто одиночный элемент.
- "Сливаем" два упорядоченных массива в один.
Слияние двух массивов
Допустим, у нас есть два отсортированных массива А и B размерами и соответственно, и мы хотим объединить их элементы в один большой отсортированный массив C размером . Для этого можно применить процедуру слияния. Эта процедура заключается в том, что мы сравниваем элементы массивов (начиная с начала) и меньший из них записываем в финальный. И затем, в массиве у которого оказался меньший элемент, переходим к следующему элементу и сравниваем теперь его. В конце, если один из массивов закончился, мы просто дописываем в финальный другой массив. После мы наш финальный массив записываем заместо двух исходных и получаем отсортированный участок.
Алгоритм слияния формально можно записать следующим образом:
// слияние двух массивов с помощью временного
merge(array a, int left, int middle, int right) // left - левая граница, right - правая, middle - середина
array b = a[middle + 1, right];
i = left, j = middle + 1, k = 0;
array temp;
while i <= middle and j <= right
temp[k++] = (a[j] < b[i]) ? a[j++] : b[i++];
while i <= middle
temp[k++] = b[i++];
while j <= right
temp[k++] = a[j++];
for (int t = 0; t != k; t++)
a[t] = temp[t]
// в конце a[1..k] это будет отсортированный массив
Рекурсивный алгоритм
Функция сортирует участок массива от элемента с номером left до элемента с номером right:
right и left — правая и левая граница массива, middle — середина.
Условие выхода — если массив стал состоять из 1 элемента.
sort(array a, int left, int right)
middle = right / 2
if middle == right
return
sort(a, left, middle)
sort (a, middle + 1, right)
merge(array a, left, middle, right)
Пример работы алгоритма показан на рисунке:
Восходящая сортировка слиянием
Помимо рекурсивного алгоритма существует и альтернативный. Пример работы алгоритма показан на рисунке:
- Выделим память размером с занимаемой памяти исходного массива.
- Попарно сравним элементы, записывая во временную память.
- Поменяем указатели временного и исходного массива.
- Выполним слияние "кусочков" размером два.
- Повторяем до тех пор, пока не сделаем единый кусок.
- Удаляем временный массив.
Процедуру слияния надо будет изменить, так, что-бы она записывала результат в результирующий массив (mas1)
sort(array * mas, int elementsAmount)
array * mas1 = new array[elementsAmount];
for(int size = 1; size < elementsAmount; size *= 2) {
int start = 0;
for(; (start + size) < elementsAmount; start += size * 2)
merge(mas + start, mas + start,
mas + start + size,
mas + start + size + min(size,elementsAmount - start - size));
for(; start < elementsAmount; ++start)
mas1[start] = mas[start];
array * temp = mas1;
mas1 = mas;
mas = temp;
delete[] mas1;
Время работы
Чтобы оценить время работы этого алгоритма, составим рекуррентное соотношение. Пускай — время сортировки массива длины n, тогда для сортировки слиянием справедливо
( — это время, необходимое на то, чтобы слить два массива). Распишем это соотношение:
Осталось оценить . Мы знаем, что , а значит . Уравнение примет вид . Так как — константа, то .
