Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Структурные элементы данных алгоритмы поиска и сортировки. Учебное пособие для СПО

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
21
Удаление элемента состоит в исключении узла из связного списка, что никак не влияет на эффективность не только алгоритма поиска, но и алгоритма включения.
Основным недостатком метода цепочек является то, что для указателей требуется дополнительная память.
1.5. КЛАССИЧЕСКИЕ АЛГОРИТМЫ ВНУТРЕННЕЙ СОРТИРОВКИ
Сортировка — это процесс расстановки элементов в некотором поряд- ке [10].
В общем виде задачу сортировки можно сформулировать следующим об­разом: имеется некоторое неупорядоченное входное множество ключей и долж­ны получить выходное множество тех же ключей, упорядоченных по возраста­нию или убыванию в численном или лексикографическом порядке.
Методы сортировки делятся на внутренние и внешние [11].
Внутренние методы предполагают, что сортируемые данные целиком располагаются в оперативной памяти.
Внешние методы используются для сортировки файлов данных, которые слишком велики, чтобы полностью поместиться в оперативной памяти.
1.5.1. Сортировка с помощью прямого обмена
Сортировка с помощью прямого обмена (сортировка стандартным обме­ном, сортировка методом пузырька) перемещает один элемент массива S в со­ответствующую позицию при каждом просмотре [12].
При первом просмотре каждый элемент Si сравнивается с элементом S
i + 1
(0 ≤ i ≤ n – 2) и при необходимости, а именно если Si > S
i + 1
, меняется с ним ме-
стами. В результате наибольший элемент помещается в последнюю позицию.
При втором просмотре выполняются те же действия, но уже не для n, а для (n – 1) первых элементов массива. В результате следующий по величине элемент перемещается в предпоследнюю позицию.
При третьем просмотре рассматриваются уже первые (n – 2) элементы массива. Таким образом, для выполнения сортировки требуется максимальное количество (n) просмотров (из-за контрольного).
ВНИМАНИЕ! Во время каждого просмотра необходимо фиксировать наличие обменов. Если при очередном просмотре обменов не было, то массив уже упорядочен, сортировка заканчивается.
22
Ниже рис. 1.8 демонстрирует выполнение сортировки методом прямого обмена для массива из 10 элементов.
Исходный массив
29
68
52
12
66
89
57
20
97
21 Проход 1
29
52
12
66
68
57
20
89
21
97
Проход 2
29
12
52
66
57
20
68
21
89
97
Проход 3
12
29
52
57
20
66
21
68
89
97
Проход 4
12
29
52
20
57
21
66
68
89
97
Проход 5
12
29
20
52
21
57
66
68
89
97
Проход 6
12
20
29
21
52
57
66
68
89
97
Проход 7
12
20
21
29
52
57
66
68
89
97
Проход 8
12
20
21
29
52
57
66
68
89
97
Рис. 1.8. Сортировка методом прямого обмена
Число сравнений: минимальное — (n – 1), максимальное — n(n – 1) / 2 (или n2 / 2).
Число обменов: минимальное — 0, среднее — (n2 / 4), максимальное — (n2 / 2).
Следовательно, алгоритм сортировки методом прямого обмена имеет
сложность O(n2).
То обстоятельство, что в данном алгоритме всегда сравниваются и пере­мещаются только соседние элементы, делает пузырьковую сортировку удобной для обработки связных списков. Перестановка в связных списках также получа­ется более экономной.
1.5.2. Сортировка с помощью прямого выбора
Сортировка с помощью прямого выбора включает в себя следующие шаги:
1. Среди элементов массива S1, …, Sn выбрать элемент с наибольшим зна-
чением.
2. Найденный элемент поменять местами с элементом Sn.
3. Выполнить шаги 1 и 2 для оставшихся n – 1 элементов, n – 2 элементов
и т. д. до тех пор, пока не останется один элемент — самый маленький [13].
23
Рис. 1.9 демонстрирует выполнение сортировки с помощью прямого вы­бора для массива из 10 элементов.
Исходный массив
29
68
52
12
66
89
57
20
97
21
Проход 1
29
68
52
12
66
89
57
20
21
97
Проход 2
29
68
52
12
66
21
57
20
89
97
Проход 3
29
20
52
12
66
21
57
68
89
97
Проход 4
29
20
52
12
57
21
66
68
89
97
Проход 5
29
20
52
12
21
57
66
68
89
97
Проход 6
29
20
21
12
52
57
66
68
89
97
Проход 7
12
20
21
29
52
57
66
68
89
97
Проход 8
12
20
21
29
52
57
66
68
89
97
Проход 9
12
20
21
29
52
57
66
68
89
97
Рис. 1.9. Сортировка методом прямого выбора
Число сравнений не зависит от начального порядка элементов и равно (n2 – n) / 2.
Число перестановок не зависит от начального порядка элементов и рав- но (n – 1).
Алгоритм сортировки методом прямого выбора имеет сложность O(n2).
Данный метод сортировки имеет важное преимущество. Поскольку каж­дый элемент передвигается не более чем один раз, то этот метод очень хорош для больших записей с маленькими ключами.
1.5.3. Сортировка с помощью прямого включения
Сортировка с помощью прямого включения (сортировка вставками) осно­вана на последовательной вставке элементов в уже упорядоченную последова­тельность.
Алгоритм сортировки заключается в следующем. Сначала в упорядочен­ную последовательность входит один первый элемент. Второй элемент включа­ется в эту упорядоченную последовательность либо перед первым, либо после первого элемента. Далее третий элемент включается в нужное место уже упо-
24
рядоченной последовательности из двух элементов, за ним — четвертый и т. д. до n-го [6].
Пусть необходимо вставить i-й (i ≥ 1) элемент в уже упорядоченную по­следовательность из i – 1 элемента. Элемент Si последовательно сравнивается с каждым элементом Sk (i – 1 до 1) и либо вставляется на свободное место (пе­ред Sk), если Si > Sk, либо элемент Sk сдвигается на одну позицию вправо — и процесс выполняется для элемента S
k – 1
.
Весь процесс вставки элемента заканчивается либо когда найден первый элемент Sk, такой, что Sk < Si, либо когда достигнута левая граница упорядочен­ной последовательности (элемент Si меньше всех элементов упорядоченной по­следовательности).
Очевидным улучшением описанного выше процесса является установка барьера в нулевом элементе массива S (S0 = Si), что позволит избежать проверки на выход за левую границу массива.
Рис. 1.10 демонстрирует выполнение сортировки с помощью прямого включения для массива из 10 элементов.
Исходный массив
29
68
52
12
66
89
57
20
97
21 Барьер
Проход 1
68
29
68
52
12
66
89
57
20
97
21
Проход 2
52
29
52
68
12
66
89
57
20
97
21
Проход 3
12
12
29
52
68
66
89
57
20
97
21
Проход 4
66
12
29
52
66
68
89
57
20
97
21
Проход 5
89
12
29
52
66
68
89
57
20
97
21
Проход 6
57
12
29
52
57
66
68
89
20
97
21
Проход 7
20
12
20
29
52
57
66
68
89
97
21
Проход 8
97
12
20
29
52
57
66
68
89
97
21
Проход 9
21
12
20
21
29
52
57
66
68
89
97
Рис. 1.10. Сортировка методом прямого включения
Число сравнений: минимальное — (n – 1), среднее — (n2 / 4), максималь­ное — (n2 / 2).
Число перемещений: минимальное — 0, среднее — (n2 / 4), максималь­ное — (n2 / 2).
25
Алгоритм сортировки методом прямого включения имеет сложность
O(n2).
Так как алгоритм требует большого числа перемещений, при значитель­ном объеме одной записи эффективность может определяться не количеством операций сравнения, а именно количеством перемещений.
1.6. ЭФФЕКТИВНЫЕ АЛГОРИТМЫ ВНУТРЕННЕЙ СОРТИРОВКИ
1.6.1. Линейная сортировка (сортировка подсчетом)
Если количество элементов в массиве немногим меньше максимального
их значения, то наиболее эффективным и по быстродействию, и по простоте написания программы является алгоритм линейной сортировки [12]. Рас­смотрим этот алгоритм более подробно.
Пусть задан массив А из n элементов и значение каждого элемента в мас­сиве А не превышает некоторое значение m (m ≤ n). Тогда алгоритм линейной сортировки будет состоять из следующих шагов:
1. Обнулить все элементы вспомогательного массива счетчиков B(m).
(Если m неизвестно, то массив B может состоять из n элементов.)
2. Пройдя по массиву А, выполнить следующие действия. Если A(i) = k, то
B(k) = B(k) + 1.
3. Проходя по массиву В, выполнить следующие действия. Если B(i) = p,
то в массив А записать p раз значение i.
Как видно из алгоритма, его сложность определяется как O(3n).
Ниже приведен пример работы алгоритма для массива из 10 элементов (рис. 1.11).
Индекс
0 1 2 3 4 5 6 7 8
9
Массив A
8 3 5 3 9 1 8 4 3
1
Массив B
0 2 0 3 1 1 0 0 2 1 Отсортированный массив A
1 1 3 3 3 4 5 8 8
9
Рис. 1.11. Линейная сортировка
26
1.6.2. Сортировка методом Шелла
Для получения метода сортировки, значительно лучшего по сложности, чем классические алгоритмы сортировки, необходим некоторый механизм, с помощью которого элементы могли бы перемещаться за один раз (за одно сравнение) на бóльшие расстояния.
Такой метод был предложен Д.Л. Шеллом и называется сортировкой с убывающим шагом, сортировкой с помощью включения с уменьшающимися расстояниями или просто сортировкой Шелла [12].
Как видно из названия, сортировка Шелла является расширением и улуч­шением сортировки с помощью прямого включения.
Основная идея этого алгоритма заключается в том, чтобы вначале устра­нить массовый беспорядок в массиве, сравнивая и переставляя далеко стоящие друг от друга элементы. Далее интервал между сравниваемыми элементами по­степенно уменьшается и, в конце концов, становится равным единице.
Формально суть метода заключается в следующем. Весь исходный массив
S разбивается на группы с шагом h1. В первую группу войдут элементы S1, S
h1 + 1
,
S
2h1 + 1
, …, во вторую — S2, S
h1 + 2
, S
2h1 + 2
, … и т. д. Затем каждая из h1 групп сор-
тируется методом прямого включения.
Далее описанный выше процесс повторяется для шага h2 (h2 < h1), h3 (h3 < h2) и т. д. Процесс заканчивается после того, как будет выполнена сор­тировка с шагом ht = 1.
Такой метод гарантированно дает упорядоченную последовательность элементов, так как на последнем шаге весь массив сортируется обычной сорти­ровкой с помощью прямого включения.
Эффективность работы алгоритма достигается за счет того, что на каждом проходе либо сортируется относительно малое число элементов, либо элементы уже довольно хорошо упорядочены и требуется сравнительно немно­го перестановок.
Однако эффективность алгоритма во многом зависит от выбора последо­вательности h1, h2, …, ht (ht = 1, h
i + 1
< hi).
Теоретикам не удалось найти оптимальную схему выбора шагов. Так, Кнут провел множество экспериментов и предложил следующую формулу выбора шагов (h) для массива длины N: в последовательности h1 = 1, h
s + 1
=
= 3hs + 1, … взять hi такое, что h
i + 2
≥ N.
27
Вот несколько первых значений h:
h h h h h
1
2
3
4
5
1
3 1 1 4 3 4 1 13 3 13 1 40 3 40 1 121
=
= + = = + = = + = = + =
( ) ( ) ( ) ( )
.
Чтобы отсортировать массив длиной 100, прежде всего найдем номер k, для которого hk  100. Согласно приведенным цифрам, k = 5. Нужное нам зна­чение находится двумя строчками выше. Таким образом, последовательность шагов при сортировке будет такой: 13–4–1. Очевидно, что хранить эту последо­вательность не нужно: очередное значение h находится из предыдущего по формуле:
h
k – 1
= [hk / 3].
Еще одну формулу для вычисления количества проходов t и последова­тельности шагов h1, …, ht предлагает Н. Вирт:
t = log2n – 1, ht = 1, h
k – 1
= 2hk + 1.
Эта формула дает следующие значения: ht = 1, h
t – 1
= 3, h
t – 2
= 7, h
t – 3
= 15,
h
t – 4
= 31 и т. д.
Для массива из 20 элементов t = 3, h1 = 7, h2 = 3, h3 = 1. Работа алгоритма рассмотрена ниже на примере (рис. 1.12). Группы выделены одним цветом.
Как показывают оценки, алгоритм сортировки Шелла имеет сложность O(n × logn), что значительно лучше рассмотренных выше алгоритмов.
Сортировка Шелла является неустойчивой сортировкой по месту. Эффек­тивность метода Шелла объясняется тем, что сдвигаемые элементы быстро по­падают на нужные места.
28
Исход­ный массив
19 10 68 99 55 11 68 30 56 8 3
8
91 35 6 3
7
6 7
7
30 75 9 6
Индексы
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 2 0
Проход 1
7
1 0
7
8
55 11 63 19 56 68 30 75 35 68 30 67 83 99 91 9 6
Проход 2
7
1 0
7
8
19 11 35 30 30 63 55 56 67 68 75 68 83 99 91 9 6
Проход 3
7 7 8
10 11 19 30 30 35 55 56 63 67 68 68 75 83 91 96 9 9
Рис. 1.12. Сортировка Шелла
1.6.3. Быстрая сортировка
Один из наиболее известных алгоритмов сортировки — быстрая сорти­ровка, предложенная Ч. Хоором [7].
Алгоритм быстрой сортировки обладает следующими привлекательны- ми особенностями: он принадлежит к категории обменных сортировок (ис­пользует лишь небольшой вспомогательный стек), на упорядочение N элемен­тов в среднем затрачивает время, пропорциональное N × log(N).
Недостаток данного метода в том, что он неустойчив, выполняет в худ­шем случае N2 операций и ненадежен в том смысле, что простая ошибка в реа­лизации может остаться незамеченной, но существенно понизить производи­тельность на некоторых видах последовательностей. Кроме того, ему требуется стек, т. е. он не является и методом сортировки на месте.
Быстрая сортировка обычно работает быстрее любого другого метода сортировки на большинстве компьютеров, поэтому широко используется как библиотечная программа сортировки и в других серьезных приложениях сорти­ровки.
Быстрая сортировка функционирует по принципу «разделяй и властвуй». Она разбивает сортируемый массив на две части, затем сортирует эти части независимо друг от друга. Как будет показано далее, точное положение точки разбиения зависит от первоначального порядка элементов во входной последо­вательности. Суть метода заключается в процессе разбиения, который переупо­рядочивает массив таким образом, что выполняются следующие три условия:
1. Элемент a[i] для некоторого i занимает свою окончательную позицию
в массиве.
29
2. Ни один из элементов a[1], …, a[i – 1] не является больше, чем a[i].
3. Ни один из элементов a[i + 1], …, a[n] не является меньше, чем a[i].
Полная упорядоченность достигается разбиением последовательности на подпоследовательности с последующим рекурсивным применением к ним этого же метода. Поскольку процесс разбиения всегда помещает хотя бы один элемент в окончательную позицию, нетрудно вывести по индукции фор­мальное доказательство того, что этот рекурсивный метод достигает нужного результата.
Основная идея алгоритма состоит в том, что случайным образом выбира­ется некоторый элемент массива x, после чего массив просматривается слева, пока не встретится элемент Si такой, что Si > x, а затем массив просматривается справа, пока не встретится элемент Sj такой, что Sj < x. Эти два элемента меня­ются местами, и процесс просмотра, сравнения и обмена продолжается, пока не пересекутся i и j. После перекрещивания индексов все, что остается сделать, чтобы завершить процесс разбиения, — это обменять центральный элемент с крайним левым элементом правой подпоследовательности.
В результате массив окажется разбитым на две части: левую, в которой значения ключей будут меньше x, и правую, в которой значения ключей будут больше x. Далее процесс рекурсивно продолжается для левой и правой частей массива до тех пор, пока каждая часть не будет содержать в точности один элемент.
Рекурсию можно заменить итерациями, если запоминать соответствую­щие индексы массива.
Таким образом, алгоритм разбивает сортируемый массив на разделы, за­тем рекурсивно сортирует каждый раздел.
В качестве примера (рис. 1.13) рассмотрим сортировку уже рассматрива­емого ранее массива (сначала в качестве центрального возьмем крайне правый элемент).
1 2 3 4 5 6 7 8 9
10
29
68
52
12
66
89
57
20
97
21
20
12
21
68
66
89
57
29
97
52
12
20
21
29
52
89
57
68
97
66
12
20
21
29
52
57
66
68
97
89
12
20
21
29
52
57
66
68
89
97
Рис. 1.13. Центральный элемент — крайне правый
30
Теперь в качестве примера рассмотрим сортировки с центральным самым левым элементом (рис. 1.14).
1 2 3 4 5 6 7 8 9
10
29
68
52
12
66
89
57
20
97
21
12
21
20
29
66
89
57
52
97
68
12
21
20
29
57
52
66
89
97
68
12
20
21
29
52
57
66
68
89
97
Рис. 1.14. Центральный элемент — крайне левый
Быстрая сортировка крайне неэффективна на некоторых простых после­довательностях, которые могут встретиться на практике. Например, если она применяется для сортировки уже отсортированной последовательности разме­ром N, то все разбиения будут вырожденными и программа вызовет себя N раз, удаляя только по одному элементу за каждый вызов.
Такая же картина возникает, когда имеется последовательность с боль­шим количеством повторяющихся ключей, что довольно часто встречается в различных приложениях.
Быстрая сортировка в худшем случае выполняет порядка N2 / 2 сравне­ний. Все разбиения также являются вырожденными и для обратно упорядочен­ных последовательностей, и для тех, которые редко встречаются на практике.
Наилучшим для быстрой сортировки является случай, когда на каждой стадии разбиения последовательность делится точно пополам.
1.7. ОЧЕРЕДИ С ПРИОРИТЕТАМИ
Во многих приложениях требуется обработка записей в порядке возраста­ния их ключей, но не обязательно в строгом порядке и не обязательно всех сразу. Часто записи накапливаются в некотором наборе, затем обрабатывается запись с максимальным ключом, после чего, возможно, продолжается накопление запи­сей, потом обрабатывается запись с наибольшим текущим ключом и т. д.
Соответствующая структура данных в такой среде поддерживает опера­ции вставки нового элемента и удаления наибольшего элемента. Такая структу­ра данных называется очередью с приоритетами [5].
В качестве примера очереди с приоритетом можно рассмотреть список задач работника. Когда он заканчивает одну задачу, то переходит к очередной, самой приоритетной, то есть выполняет операцию извлечения максимума. Ру-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]