Сортировка вставками
Сортировка вставками — квадратичный алгоритм сортировки.
Алгоритм
<wikitex>Задача заключается в следующем: есть часть массива, которая уже отсортирована, и требуется вставить остальные элементы массива в отсортированную часть, сохранив при этом упорядоченность. Для этого на каждом шаге алгоритма мы выбираем один из элементов входных данных и вставляем его на нужную позицию в уже отсортированной части массива, до тех пор пока весь набор входных данных не будет отсортирован. Метод выбора очередного элемента из исходного массива произволен, однако обычно (и с целью получения устойчивого алгоритма сортировки), элементы вставляются по порядку их появления во входном массиве.
Так как в процессе работы алгоритма могут меняться местами только соседние элементы, каждый обмен уменьшает число инверсий на единицу. Следовательно, количество обменов равно количеству инверсий в исходном массиве вне зависимости от реализации сортировки. Максимальное количество инверсий содержится в массиве, элементы которого отсортированы по невозрастанию. Число инверсий в таком массиве .
Алгоритм работает за $O(n + k)$, где k — число обменов элементов входного массива, равное числу инверсий. В среднем и в худшем случае — за $O(n^2)$. Минимальные оценки встречаются в случае уже упорядоченной исходной последовательности элементов, наихудшие — когда они расположены в обратном порядке. </wikitex>
Псевдокод
InsertionSort(a)
for i = 1 to n - 1:
j = i - 1
while (j
0) and (a[j] > a[j + 1]):
swap(a[j], a[j + 1])
j = j - 1
Пример работы
Пример работы алгоритма для массива
До | После | Описание шага |
---|---|---|
Первый проход (проталкиваем второй элемент — 2) | ||
5 2 4 3 1 | 2 5 4 3 1 | Алгоритм сравнивает второй элемент с первым и меняет их местами. |
Второй проход (проталкиваем третий элемент — 4) | ||
2 5 4 3 1 | 2 4 5 3 1 | Сравнивает третий со вторым и меняет местами |
2 4 5 3 1 | 2 4 5 3 1 | Второй и первый отсортированы, swap не требуется |
Третий проход (проталкиваем четвертый — 3) | ||
2 4 5 3 1 | 2 4 3 5 1 | Меняет четвертый и третий местами |
2 4 3 5 1 | 2 3 4 5 1 | Меняет третий и второй местами |
2 3 4 5 1 | 2 3 4 5 1 | Второй и первый отсортированы, swap не требуется |
Четвертый проход (проталкиваем пятый элемент — 1) | ||
2 3 4 5 1 | 2 3 4 1 5 | Меняет пятый и четвертый местами |
2 3 4 1 5 | 2 3 1 4 5 | Меняет четвертый и третий местами |
2 3 1 4 5 | 2 1 3 4 5 | Меняет третий и второй местами |
2 1 3 4 5 | 1 2 3 4 5 | Меняет второй и первый местами. Массив отсортирован. |
Оптимизации
Бинарные вставки
Так как в среднем количество сравнений для
-го элемента равно , следовательно общее количество сравнений приблизительно , но это очень много даже при малых . Суть этого заключается в том, что поиск позиции для вставки -го элемента осуществляется бинарным поиском, вследствие чего количество сравнений для элементов . Количество сравнений заметно уменьшилось, но для того, чтобы поставить элемент на -тое место, всё ещё необходимо переместить элементов. В итоге время выполнения алгоритма уменьшилось в среднем в два раза : , следовательно .InsertionSort'(a) for i = 1 to n - 1: j = i - 1 k = BinSearch(a, a[i], 0, j) swap(a[k], a[i])
Двухпутевые вставки
Суть этого метода в том, что вместо отсортированной части массива мы используем область вывода. Первый элемент помещается в середину области вывода, а место для последующих элементов освобождается потём сдвига элементов влево или вправо туда, куда выгоднее. Пример для набора элементов
До | После | Описание шага |
---|---|---|
Первый проход (проталкиваем второй элемент — 5) | ||
5 | Так как в поле вывода нет элементов то мы просто добавляем элемент туда. | |
Второй проход (проталкиваем третий элемент — 7) | ||
5 | 5 7 | С помощью Бинарного поиска находим позицию и так как позиция крайняя то сдвигать ничего не приходится. |
Третий проход (проталкиваем четвертый — 3) | ||
5 7 | 3 5 7 | С помощью Бинарного поиска находим позицию и так как позиция крайняя то сдвигать ничего не приходится. |
Четвертый проход (проталкиваем пятый элемент — 4) | ||
3 5 7 | 3 4 5 7 | С помощью Бинарного поиска находим позицию. Расстояние до левого края зоны вывода меньше ем до правого то сдвигаем левую часть. |
Четвертый проход (проталкиваем пятый элемент — 6) | ||
3 4 5 7 | 3 4 5 6 7 | Расстояние до правого края меньше чем до левого, следовательно двигаем правую часть. |
Как можно заметить структура поля вывода имеет сходство с Двусвязной очередью, а именно мы выбираем край к которому ближе наш элемент, затем добавляем с этой стороны наш элемент и двигаем его. Как мы видим в этом примере понадобилось сдвинуть всего 3 элемента. Время выполнения алгоритма сократилось в четыре раза, благодаря тому что теперь мы вместо перемещения в среднем
мы перемещаем элементов : , следовательно .Вставка в список
При этой сортировке мы используем односвязной список вместо упорядоченной части массива. Теперь на не потребуется времени на сдвиг, а понадобиться всего лишь изменить родственные связи. При этом время выполнения сокращается в два раза :
, следовательно .Сортировка с вычислением адреса
Сортировку с вычисление адреса выгодно использовать когда ключи распределены равномерно и не скапливаются хаотично в отдельных диапазонах значений. Вместо левой отсортированной части массива мы будем использовать
односвязных списков, в каждом из которых будут храниться значения из определённого диапазона. С каждым списком работаем как при простых выставках в список. Вероятность того что элемент попадёт в какой-либо список , следовательно для каждого элемента происходит примерно сравнений, следовательно общее количество сравнений , а при порядка асимптотическая сложность уменьшается до . Рассмотрим на примере. Входные данные : Будем использовать 4 списка с соответствующими диапазонами значений : .3 элемента | 6 элементов | 9 элементов | |
0 - 250 | 123 245 | 123 245 | |
251 - 500 | 345 | 345 | 300 345 490 |
501 - 750 | 743 | 550 743 | |
751 - 1000 | 867 984 | 867 984 | 867 984 |
См. также
Источники
- Сортировка вставками — Википедия
- Н. Вирт «Алгоритмы и структуры данных», часть 2.2.1 "Сортировка с помощью прямого включения"