Сортировка кучей — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (Реализация)
(Реализация)
(не показано 13 промежуточных версий 3 участников)
Строка 2: Строка 2:
  
 
== Алгоритм ==
 
== Алгоритм ==
Необходимо отсортировать массив <tex>A</tex>, размером <tex>n</tex>. Построим на базе этого массива за <tex>O(n)</tex> кучу для максимума. Так как максимальный элемент находится в корне, то,если поменять его местами с <tex>A[n - 1]</tex>, он встанет на свое место. Далее вызовем процедуру <tex> \mathrm{siftDown(0)} </tex>, предварительно уменьшив <tex> \mathrm{heapSize} </tex> на <tex>1</tex>. Она за <tex>O(\log{n})</tex> просеет <tex>A[0]</tex> на нужное место и сформирует новую кучу (так как мы уменьшили ее размер, то куча располагается с <tex>A[0]</tex> по <tex>A[n - 2]</tex>, а элемент <tex>A[n-1]</tex> находится на своем месте). Повторим эту процедуру для новой кучи, только корень будет менять местами не с <tex>A[n - 1]</tex>, а с <tex>A[n-2]</tex>. Делая аналогичные действия, пока <tex> \mathrm{heapSize}  </tex> не станет равен <tex>1</tex>, мы будем ставить наибольшее из оставшихся чисел в конец не отсортированной части. Очевидно, что таким образом, мы получим отсортированный массив.
+
Необходимо отсортировать массив <tex>A</tex>, размером <tex>n</tex>. Построим на базе этого массива за <tex>O(n)</tex> кучу для максимума. Так как максимальный элемент находится в корне, то если поменять его местами с <tex>A[n - 1]</tex>, он встанет на своё место. Далее вызовем процедуру <tex> \mathrm{siftDown(0)} </tex>, предварительно уменьшив <tex> \mathrm{heapSize} </tex> на <tex>1</tex>. Она за <tex>O(\log{n})</tex> просеет <tex>A[0]</tex> на нужное место и сформирует новую кучу (так как мы уменьшили её размер, то куча располагается с <tex>A[0]</tex> по <tex>A[n - 2]</tex>, а элемент <tex>A[n-1]</tex> находится на своём месте). Повторим эту процедуру для новой кучи, только корень будет менять местами не с <tex>A[n - 1]</tex>, а с <tex>A[n-2]</tex>. Делая аналогичные действия, пока <tex> \mathrm{heapSize}  </tex> не станет равен <tex>1</tex>, мы будем ставить наибольшее из оставшихся чисел в конец не отсортированной части. Очевидно, что таким образом, мы получим отсортированный массив.
  
 
== Реализация ==
 
== Реализация ==
 
*<tex>\mathrm{A}</tex> {{---}} массив, который необходимо отсортировать
 
*<tex>\mathrm{A}</tex> {{---}} массив, который необходимо отсортировать
*<tex>\mathrm{n}</tex> {{---}} количество элементов в нем
+
*<tex>\mathrm{n}</tex> {{---}} количество элементов в нём
 
*<tex> \mathrm{buildHeap(A)} </tex> {{---}} процедура, которая строит из передаваемого массива кучу для максимума в этом же массиве
 
*<tex> \mathrm{buildHeap(A)} </tex> {{---}} процедура, которая строит из передаваемого массива кучу для максимума в этом же массиве
*<tex> \mathrm{siftDown(A, i, len)} </tex> {{---}} процедура, которая просеивает вниз элемент <tex> \mathrm{A[i]} </tex> в куче из <tex> \mathrm{len} </tex> элементов, находящихся в начале. массива <tex> \mathrm{A} </tex>
+
*<tex> \mathrm{siftDown(A, i, len)} </tex> {{---}} процедура, которая просеивает вниз элемент <tex> \mathrm{A[i]} </tex> в куче из <tex> \mathrm{len} </tex> элементов, находящихся в начале массива <tex> \mathrm{A} </tex>
   '''fun''' heapsort(A : '''list <T>'''):
+
   '''fun''' heapSort(A : '''list <T>'''):
 
     buildHeap(A)
 
     buildHeap(A)
 
     heapSize = A.size
 
     heapSize = A.size
     '''for''' i = 0 '''to''' n - 2
+
     '''for''' i = 0 '''to''' n - 1
 
       swap(A[0], A[n - 1 - i])
 
       swap(A[0], A[n - 1 - i])
 
       heapSize--
 
       heapSize--
       siftDown(A, 0, heap_size)
+
       siftDown(A, 0, heapSize)
  
 
== Сложность ==
 
== Сложность ==
Строка 21: Строка 21:
  
 
Достоинства:
 
Достоинства:
* Худшее время работы {{---}} <tex>O(n\log{n})</tex>.
+
* худшее время работы {{---}} <tex>O(n\log{n})</tex>,
* Требует <tex>O(1)</tex> дополнительной памяти.
+
* требует <tex>O(1)</tex> дополнительной памяти.
 
Недостатки:
 
Недостатки:
* Неустойчивая
+
* неустойчивая,
* На почти отсортированных данных работает столь же долго, как и на хаотических данных.
+
* на почти отсортированных данных работает столь же долго, как и на хаотических данных.
  
 
== Пример ==
 
== Пример ==
Строка 37: Строка 37:
 
  |[[Файл:heap4.png|155px|thumb|Второй проход]]
 
  |[[Файл:heap4.png|155px|thumb|Второй проход]]
 
  |[[Файл:heap5.png|155px|thumb|Третий проход]]
 
  |[[Файл:heap5.png|155px|thumb|Третий проход]]
  |[[Файл:heap6.png|155px|thumb|Четвертый проход]]
+
  |[[Файл:heap6.png|155px|thumb|Четвёртый проход]]
 
  |}
 
  |}
  
Строка 55: Строка 55:
 
|-
 
|-
 
|style="background-color:#FFF;padding:2px 10px"| '''4''' 3 2 1 5
 
|style="background-color:#FFF;padding:2px 10px"| '''4''' 3 2 1 5
|style="background-color:#FFF;padding:2px 10px"| Строим кучу из первых четырех элементов
+
|style="background-color:#FFF;padding:2px 10px"| Строим кучу из первых четырёх элементов
 
|-
 
|-
 
|colspan=3|''Второй проход''
 
|colspan=3|''Второй проход''
 
|-
 
|-
 
|style="background-color:#FFF;padding:2px 10px"| '''1''' 3 2 '''4''' 5
 
|style="background-color:#FFF;padding:2px 10px"| '''1''' 3 2 '''4''' 5
|style="background-color:#FFF;padding:2px 10px"| Меняем местами первый и четвертый элементы  
+
|style="background-color:#FFF;padding:2px 10px"| Меняем местами первый и четвёртый элементы  
 
|-
 
|-
 
|style="background-color:#FFF;padding:2px 10px"| '''3''' 1 2 4 5
 
|style="background-color:#FFF;padding:2px 10px"| '''3''' 1 2 4 5
|style="background-color:#FFF;padding:2px 10px"| Строим кучу из первых трех элементов
+
|style="background-color:#FFF;padding:2px 10px"| Строим кучу из первых трёх элементов
 
|-
 
|-
 
|colspan=3|''Третий проход''
 
|colspan=3|''Третий проход''
Строка 73: Строка 73:
 
|style="background-color:#FFF;padding:2px 10px"| Строим кучу из двух элементов
 
|style="background-color:#FFF;padding:2px 10px"| Строим кучу из двух элементов
 
|-
 
|-
|colspan=3|''Четвертый проход''
+
|colspan=3|''Четвёртый проход''
 
|-
 
|-
 
|style="background-color:#FFF;padding:2px 10px"| '''1''' '''2''' 3 4 5
 
|style="background-color:#FFF;padding:2px 10px"| '''1''' '''2''' 3 4 5
Строка 87: Строка 87:
 
= JSort =
 
= JSort =
 
'''JSort''' является модификацией сортировки кучей, которую придумал Джейсон Моррисон (''Jason Morrison'').
 
'''JSort''' является модификацией сортировки кучей, которую придумал Джейсон Моррисон (''Jason Morrison'').
Алгоритм частично упорядочивает массив, строя на нем два раза кучу: один раз передвигая меньшие элементы влево, второй раз передвигая большие элементы вправо. Затем к массиву применяется
+
Алгоритм частично упорядочивает массив, строя на нём два раза кучу: один раз передвигая меньшие элементы влево, второй раз передвигая большие элементы вправо. Затем к массиву применяется
[[Сортировка вставками|сортировка вставками]], которая при почти отсортированных данных работает за <tex>O(n)</tex>
+
[[Сортировка вставками|сортировка вставками]], которая при почти отсортированных данных работает за <tex>O(n)</tex>.
  
 
Достоинства:
 
Достоинства:
отличии от сортировки кучей, на почти отсортированных массивах работает быстрее, чем на случайных.
+
отличие от сортировки кучей, на почти отсортированных массивах работает быстрее, чем на случайных.
 
*В силу использования сортировки вставками, которая просматривает элементы последовательно, использование кэша гораздо эффективнее.
 
*В силу использования сортировки вставками, которая просматривает элементы последовательно, использование кэша гораздо эффективнее.
 
Недостатки:
 
Недостатки:
Строка 99: Строка 99:
 
Построим кучу для минимума на этом массиве.  
 
Построим кучу для минимума на этом массиве.  
 
Тогда наименьший элемент окажется на первой позиции, а левая часть массива окажется почти отсортированной, так как ей будут соответствовать верхние узлы кучи.
 
Тогда наименьший элемент окажется на первой позиции, а левая часть массива окажется почти отсортированной, так как ей будут соответствовать верхние узлы кучи.
Теперь построим на этом же массиве кучу так, чтобы немного упорядочить правую часть массива. Эта куча должна быть кучей для максимума и быть "зеркальной" к массиву, то есть чтобы ее корень соответствовал последнему элементу массива.
+
Теперь построим на этом же массиве кучу так, чтобы немного упорядочить правую часть массива. Эта куча должна быть кучей для максимума и быть "зеркальной" к массиву, то есть чтобы её корень соответствовал последнему элементу массива.
 
К получившемуся массиву применим сортировку вставками.
 
К получившемуся массиву применим сортировку вставками.
  
 
=== Сложность ===
 
=== Сложность ===
  
Постройка кучи занимает <tex>O(n)</tex>. Почти упорядоченный массив сортировка вставками может отсортировать <tex> O(n)</tex>, но в худшем случае за <tex>O(n^2)</tex> в худшем.
+
Построение кучи занимает <tex>O(n)</tex>. Почти упорядоченный массив сортировка вставками может отсортировать <tex> O(n)</tex>, но в худшем случае за <tex>O(n^2)</tex>.
  
Таким образом в худшем случае сложность JSort является <tex>O(n^2)</tex>.
+
Таким образом, наихудшая оценка Jsort {{---}} <tex>O(n^2)</tex>.
  
 
=== Пример ===
 
=== Пример ===
Рассмотрим, массив <tex> A </tex> = <tex> (1, 2, 8, 15, 17, 20, 31, 32, 30, 2, 3, 5, 10, 11, 24 ) </tex>
+
Рассмотрим, массив <tex> A </tex> = <tex> [1, 2, 8, 15, 17, 20, 31, 32, 30, 2, 3, 5, 10, 11, 24 ] </tex>
  
 
Построим на этом массиве кучу для минимума:
 
Построим на этом массиве кучу для минимума:
Строка 115: Строка 115:
 
| [[Файл:HeapW.png|400px]]  
 
| [[Файл:HeapW.png|400px]]  
 
|}
 
|}
Сам массив же будет выглядеть следующим образом:
+
Массив выглядит следующим образом:
 
{| cellpadding="3" style="margin-left: left; margin-right: left;"
 
{| cellpadding="3" style="margin-left: left; margin-right: left;"
 
| [[Файл:HeapM.png|400px]]  
 
| [[Файл:HeapM.png|400px]]  
Строка 125: Строка 125:
 
| [[Файл:HeapWU.png|400px]]  
 
| [[Файл:HeapWU.png|400px]]  
 
|}
 
|}
Сам массив же будет выглядеть следующим образом:
+
Массив будет выглядеть следующим образом:
 
{| cellpadding="3" style="margin-left: left; margin-right: left;"
 
{| cellpadding="3" style="margin-left: left; margin-right: left;"
 
| [[Файл:HeapMU.png|400px]]  
 
| [[Файл:HeapMU.png|400px]]  

Версия 14:34, 28 октября 2019

Сортировка кучей, пирамидальная сортировка (англ. Heapsort) — алгоритм сортировки, использующий структуру данных двоичная куча. Это неустойчивый алгоритм сортировки с временем работы [math]O(n\log{n})[/math] , где [math]n[/math] — количество элементов для сортировки, и использующий [math]O(1)[/math] дополнительной памяти.

Алгоритм

Необходимо отсортировать массив [math]A[/math], размером [math]n[/math]. Построим на базе этого массива за [math]O(n)[/math] кучу для максимума. Так как максимальный элемент находится в корне, то если поменять его местами с [math]A[n - 1][/math], он встанет на своё место. Далее вызовем процедуру [math] \mathrm{siftDown(0)} [/math], предварительно уменьшив [math] \mathrm{heapSize} [/math] на [math]1[/math]. Она за [math]O(\log{n})[/math] просеет [math]A[0][/math] на нужное место и сформирует новую кучу (так как мы уменьшили её размер, то куча располагается с [math]A[0][/math] по [math]A[n - 2][/math], а элемент [math]A[n-1][/math] находится на своём месте). Повторим эту процедуру для новой кучи, только корень будет менять местами не с [math]A[n - 1][/math], а с [math]A[n-2][/math]. Делая аналогичные действия, пока [math] \mathrm{heapSize} [/math] не станет равен [math]1[/math], мы будем ставить наибольшее из оставшихся чисел в конец не отсортированной части. Очевидно, что таким образом, мы получим отсортированный массив.

Реализация

  • [math]\mathrm{A}[/math] — массив, который необходимо отсортировать
  • [math]\mathrm{n}[/math] — количество элементов в нём
  • [math] \mathrm{buildHeap(A)} [/math] — процедура, которая строит из передаваемого массива кучу для максимума в этом же массиве
  • [math] \mathrm{siftDown(A, i, len)} [/math] — процедура, которая просеивает вниз элемент [math] \mathrm{A[i]} [/math] в куче из [math] \mathrm{len} [/math] элементов, находящихся в начале массива [math] \mathrm{A} [/math]
 fun heapSort(A : list <T>):
   buildHeap(A)
   heapSize = A.size
   for i = 0 to n - 1
     swap(A[0], A[n - 1 - i])
     heapSize--
     siftDown(A, 0, heapSize)

Сложность

Операция [math] \mathrm{siftDown} [/math] работает за [math]O(\log{n})[/math]. Всего цикл выполняется [math](n - 1)[/math] раз. Таким образом сложность сортировки кучей является [math]O(n\log{n})[/math].

Достоинства:

  • худшее время работы — [math]O(n\log{n})[/math],
  • требует [math]O(1)[/math] дополнительной памяти.

Недостатки:

  • неустойчивая,
  • на почти отсортированных данных работает столь же долго, как и на хаотических данных.

Пример

Строим кучу
Первый проход
Строим новую кучу
Второй проход
Третий проход
Четвёртый проход

Пусть дана последовательность из [math]5[/math] элементов [math]3, 2, 4, 1, 5[/math].

Массив Описание шага
5 3 4 1 2 Строим кучу из исходного массива
Первый проход
2 3 4 1 5 Меняем местами первый и последний элементы
4 3 2 1 5 Строим кучу из первых четырёх элементов
Второй проход
1 3 2 4 5 Меняем местами первый и четвёртый элементы
3 1 2 4 5 Строим кучу из первых трёх элементов
Третий проход
2 1 3 4 5 Меняем местами первый и третий элементы
2 1 3 4 5 Строим кучу из двух элементов
Четвёртый проход
1 2 3 4 5 Меняем местами первый и второй элементы
1 2 3 4 5 Массив отсортирован



JSort

JSort является модификацией сортировки кучей, которую придумал Джейсон Моррисон (Jason Morrison). Алгоритм частично упорядочивает массив, строя на нём два раза кучу: один раз передвигая меньшие элементы влево, второй раз передвигая большие элементы вправо. Затем к массиву применяется сортировка вставками, которая при почти отсортированных данных работает за [math]O(n)[/math].

Достоинства:

  • В отличие от сортировки кучей, на почти отсортированных массивах работает быстрее, чем на случайных.
  • В силу использования сортировки вставками, которая просматривает элементы последовательно, использование кэша гораздо эффективнее.

Недостатки:

  • На длинных массивах, возникают плохо отсортированные последовательности в середине массива, что приводит к ухудшению работы сортировки вставками.

Алгоритм

Построим кучу для минимума на этом массиве. Тогда наименьший элемент окажется на первой позиции, а левая часть массива окажется почти отсортированной, так как ей будут соответствовать верхние узлы кучи. Теперь построим на этом же массиве кучу так, чтобы немного упорядочить правую часть массива. Эта куча должна быть кучей для максимума и быть "зеркальной" к массиву, то есть чтобы её корень соответствовал последнему элементу массива. К получившемуся массиву применим сортировку вставками.

Сложность

Построение кучи занимает [math]O(n)[/math]. Почти упорядоченный массив сортировка вставками может отсортировать [math] O(n)[/math], но в худшем случае за [math]O(n^2)[/math].

Таким образом, наихудшая оценка Jsort — [math]O(n^2)[/math].

Пример

Рассмотрим, массив [math] A [/math] = [math] [1, 2, 8, 15, 17, 20, 31, 32, 30, 2, 3, 5, 10, 11, 24 ] [/math]

Построим на этом массиве кучу для минимума:

HeapW.png

Массив выглядит следующим образом:

HeapM.png

Заметим, что начало почти упорядочено, что хорошо скажется на использовании сортировки вставками.

Построим теперь зеркальную кучу для максимума на этом же массиве.

HeapWU.png

Массив будет выглядеть следующим образом:

HeapMU.png

Теперь и конец массива выглядит упорядоченным, применим сортировку вставками и получим отсортированный массив.

См. также

Источники информации