Сортировка слиянием — различия между версиями
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;
Время работы
Чтобы оценить время работы этого алгоритма, составим рекуррентное соотношение. Пускай
( — это время, необходимое на то, чтобы слить два массива). Распишем это соотношение:
Осталось оценить
. Мы знаем, что , а значит . Уравнение примет вид . Так как — константа, то .