Алгоритм сортировки массива паскаль
Алгоритм сортировки массива паскаль
Простые и улучшенные методы упорядочения данных.
Содержание
Задача сортировки
Эта лекция посвящена сугубо алгоритмической проблеме упорядочения данных.
Необходимость отсортировать какие–либо величины возникает в программировании очень часто. К примеру, входные данные подаются «вперемешку», а вашей программе удобнее обрабатывать упорядоченную последовательность. Существуют ситуации, когда предварительная сортировка данных позволяет сократить содержательную часть алгоритма в разы, а время его работы — в десятки раз.
Однако верно и обратное. Сколь бы хорошим и эффективным ни был выбранный вами алгоритм, но если в качестве подзадачи он использует «плохую» сортировку, то вся работа по его оптимизации оказывается бесполезной. Неудачно реализованная сортировка входных данных способна заметно понизить эффективность алгоритма в целом.
Методы упорядочения подразделяются на внутренние (обрабатывающие массивы) и внешние (занимающиеся только файлами) 1 .
Эту лекцию мы посвятим только внутренним сортировкам. Их важная особенность состоит в том, что эти алгоритмы не требуют дополнительной памяти: вся работа по упорядочению производится внутри одного и того же массива.
Простые сортировки
К простым внутренним сортировкам относят методы, сложность которых пропорциональна квадрату размерности входных данных. Иными словами, при сортировке массива, состоящего из N компонент, такие алгоритмы будут выполнять С*N 2 действий, где С — некоторая константа.
Количество действий, необходимых для упорядочения некоторой последовательности данных, конечно же, зависит не только от длины этой последовательности, но и от её структуры. Например, если на вход подаётся уже упорядоченная последовательность (о чём программа, понятно, не знает), то количество действий будет значительно меньше, чем в случае перемешанных входных данных.
Как правило, сложность алгоритмов подсчитывают раздельно по количеству сравнений и по количеству перемещений данных в памяти (пересылок), поскольку выполнение этих операций занимает различное время. Однако точные значения удаётся найти редко, поэтому для оценки алгоритмов ограничиваются лишь понятием «пропорционально», которое не учитывает конкретные значения констант, входящих в итоговую формулу. Общую же эффективность алгоритма обычно оценивают «в среднем»: как среднее арифметическое от сложности алгоритма «в лучшем случае» и «в худшем случае», то есть (Eff_best + Eff_worst) / 2.
Сортировка простыми вставками
Самый простой способ сортировки 2 , который приходит в голову, — это упорядочение данных по мере их поступления. В этом случае при вводе каждого нового значения можно опираться на тот факт, что все предыдущие элементы уже образуют отсортированную последовательность.
Алгоритм ПрВст
- Первый элемент записать «не раздумывая».
- Пока не закончится последовательность вводимых данных, для каждого нового её элемента выполнять следующие действия:
— начав с конца уже существующей упорядоченной последовательности, все её элементы, которые больше, чем вновь вводимый элемент, сдвинуть на 1 шаг назад;
— записать новый элемент на освободившееся место.
При этом, разумеется, можно прочитать все вводимые элементы одновременно, записать их в массив, а потом «воображать», что каждый очередной элемент был введён только что. На суть и структуру алгоритма это не повлияет.
Реализация алгоритма ПрВст
Метод прямых вставок с барьером (ПрВстБар)
Для того, чтобы сократить количество сравнений, производимых нашей программой, дополним сортируемый массив нулевой компонентой (это следует сделать в разделе описаний var ) и будем записывать в неё поочерёдно каждый вставляемый элемент (сравните строки <*>и <**>в приведённых вариантах программы). В тех случаях, когда вставляемое значение окажется меньше, чем a[1], компонента a[0] будет работать как «барьер», не дающий индексу j выйти за нижнюю границу массива. Кроме того, компонента a[0] может заменить собою и дополнительную переменную х:
Эффективность алгоритма ПрВстБар
Понятно, что для этой сортировки наилучшим будет случай, когда на вход подаётся уже упорядоченная последовательность данных. Тогда алгоритм ПрВстБар совершит N-1 сравнение и 0 пересылок данных.
В худшем же случае — когда входная последовательность упорядочена «наоборот» — сравнений будет уже (N + 1) * N / 2, а пересылок (N — 1) * (N + 3). Таким образом, этот алгоритм имеет сложность
N 2 (читается «порядка эн квадрат») по обоим параметрам.
Пример сортировки
Предположим, что нужно отсортировать следующий набор чисел:
Выполняя алгоритм ПрВстБар, мы получим такие результаты (подчёркнута уже отсортированная часть массива, полужирным выделена сдвигаемая последовательность, а квадратиком выделен вставляемый элемент):
0 шаг: 5343621
1 шаг: 343621 1 1+1 3 1+2 4
2 шаг: 43621 1 1+1 1+2
3 шаг: 3621 2 2+1 2+2
4 шаг: 621 0 1 0
5 шаг: 21 5 5+1 5+2
6 шаг: 1
Результат: 1233456 15 20 25
Сортировка бинарными вставками
Сортировку простыми вставками можно немного улучшить: поиск «подходящего места» в упорядоченной последовательности можно вести более экономичным способом, который называется Двоичный поиск в упорядоченной последовательности. Он напоминает детскую игру «больше–меньше»: после каждого сравнения обрабатываемая последовательность сокращается в два раза.
Пусть, к примеру, нужно найти место для элемента 7 в таком массиве:
Найдём средний элемент этой последовательности (10) и сравним с ним семёрку. После этого всё, что больше 10 (да и саму десятку тоже), можно смело исключить из дальнейшего рассмотрения:
Снова возьмём середину в отмеченном куске последовательности, чтобы сравнить её с семеркой. Однако здесь нас поджидает небольшая проблема: точной середины у новой последовательности нет, поэтому нужно решить, который из двух центральных элементов станет этой «серединой». От того, к какому краю будет смещаться выбор в таких «симметричных» случаях, зависит окончательная реализация нашего алгоритма. Давайте договоримся, что новой «серединой» последовательности всегда будет становиться левый центральный элемент. Это соответствует вычислению номера «середины» по формуле
Итак, отсечём половину последовательности:
Таким образом, мы нашли в исходной последовательности место, «подходящее» для нового элемента. Если бы в той же самой последовательности нужно было найти позицию не для семёрки, а для девятки, то последовательность границ рассматриваемых промежутков была бы такой:
Из приведенных примеров уже видно, что поиск ведётся до тех пор, пока левая граница не окажется правее(!) правой границы. Кроме того, по завершении этого поиска последней левой границей окажется как раз тот элемент, на котором необходимо закончить сдвиг «хвоста» последовательности.
Будет ли такой алгоритм универсальным? Давайте проверим, что же произойдёт, если мы станем искать позицию не для семёрки или девятки, а для единицы:
Как видим, правая граница становится неопределённой — выходит за пределы массива. Будет ли этот факт иметь какие–либо неприятные последствия? Очевидно, нет, поскольку нас интересует не правая, а левая граница.
«А что будет, если мы захотим добавить 21?» — спросит особо въедливый читатель. Проверим это:
Кажется, будто всё плохо: левая граница вышла за пределы массива; непонятно, что нужно сдвигать.
Вспомним, однако, что в реальности на (N + 1)–й позиции как раз и находится вставляемый элемент (21). Таким образом, если левая граница вышла за рассматриваемый диапазон, получается, что ничего сдвигать не нужно. Вообще же, такие действия выглядят явно лишними, поэтому от них стоит застраховаться, введя одну дополнительную проверку в текст алгоритма.
Реализация алгоритма БинВст
Эффективность алгоритма БинВст
Теперь на каждом шаге выполняется не N, а log N проверок 5 , что уже значительно лучше (для примера, сравните 1000 и 10 = log 1024). Следовательно, всего будет совершено N*log N сравнений. Впрочем, улучшение это не слишком значительное, ведь по количеству пересылок наш алгоритм по–прежнему имеет сложность «порядка N 2 ».
Сортировка простым выбором
Попробуем теперь сократить количество пересылок элементов.
Алгоритм ПрВыб
На каждом шаге (всего их будет ровно N — 1) будем производить такие действия:
- найдём минимум среди всех ещё не упорядоченных элементов;
- поменяем его местами с первым «по очереди» не отсортированным элементом. Мы надеемся, что читателям очевидно, почему к концу работы этого алгоритма последний (N–й) элемент массива автоматически окажется максимальным.
Реализация ПрВыб
Эффективность алгоритма ПрВыб
В лучшем случае (если исходная последовательность уже упорядочена), алгоритм ПрВыб произведёт (N — 1) * (N + 2) / 2 сравнений и 0 пересылок данных. В остальных же случаях количество сравнений останется прежним, а вот количество пересылок элементов массива будет равным 3 * (N — 1).
Таким образом, алгоритм ПрВыб имеет квадратичную сложность (
N 2 ) по сравнениям и линейную (
N) — по пересылкам.
Замечание. Если перед вами поставлена задача отсортировать строки двумерного массива (размерности NxN) по значениям его первого столбца, то сложность алгоритма ПрВыб, модифицированного для решения этой задачи, будет квадратичной (N 2 сравнений и N 2 пересылок), а алгоритма БинВст — кубической (N*log N сравнений и N 3 пересылок). Комментарии, как говорится, излишни.
Пример сортировки
Предположим, что нужно отсортировать тот же набор чисел, при помощи которого мы иллюстрировали метод сортировки простыми вставками:
Теперь мы будем придерживаться алгоритма ПрВыб (подчёркнута несортированная часть массива, а квадратиком выделен её минимальный элемент):
1 шаг:
2 шаг: 1
3 шаг: 12 <***>6
4 шаг: 123<ничего не делаем>
5 шаг: 1233
6 шаг: 12334
результат: 1233456
Сортировка простыми обменами
Рассмотренными сортировками, конечно же, не исчерпываются все возможные методы упорядочения массивов.
Существуют, например, алгоритмы, основанные на обмене двух соседних элементов: Пузырьковая и Шейкерная сортировки. Обе имеют сложность порядка N 2 , однако и по скорости работы на любых входных данных, и по простоте реализации они проигрывают другим простым сортировкам. Поэтому мы настойчиво советуем читателю не прельщаться красивыми названиями, за которыми не стоит никакой особенной выгоды.
Тем же, кто всё-таки желает ознакомиться с обменными сортировками, а также с подробными данными по сравнению различных сортировок, мы рекомендуем труды Д. Кнута 7 или Н. Вирта 8 .
12. Методы сортировки массивов
Сортировкой или упорядочением массива называется расположение его элементов по возрастанию (или убыванию). Если не все элементы различны, то надо говорить о неубывающем (или невозрастающем) порядке.
- количество шагов алгоритма, необходимых для упорядочения;
- количество сравнений элементов;
- количество перестановок, выполняемых при сортировке.
Мы рассмотрим только три простейшие схемы сортировки.
Метод «пузырька»
По-видимому, самым простым методом сортировки является так называемый метод » пузырька «. Чтобы уяснить его идею, представьте , что массив (таблица) расположен вертикально. Элементы с большим значением всплывают вверх наподобие больших пузырьков. При первом проходе вдоль массива, начиная проход «снизу», берется первый элемент и поочередно сравнивается с последующими. При этом:
В результате наибольший элемент оказывается в самом верху массива.
Во время второго прохода вдоль массива находится второй по величине элемент, который помещается под элементом, найденным при первом проходе, т.е на вторую сверху позицию, и т.д.
Заметим, что при втором и последующих проходах, нет необходимости рассматривать ранее «всплывшие» элементы, т.к. они заведомо больше оставшихся. Другими словами, во время j -го прохода не проверяются элементы, стоящие на позициях выше j .
Теперь можно привести текст программы упорядочения массива M[1..N] :
begin for j :=1 to N -1 do for i :=1 to N — j do if M[ i ] > M[ i +1] then swap (M[ i ],M[ i +1]) end; |
Стандартная процедура swap будет использоваться и в остальных алгоритмах сортировки для перестановки элементов (их тип мы уточнять не будем) местами:
procedure swap (var x,y: . );
var t: . ;
begin
t := x;
x := y;
y := t
end;
Заметим, что если массив M глобальный, то процедура могла бы содержать только аргументы (а не результаты). Кроме того, учитывая специфику ее применения в данном алгоритме, можно свести число парметров к одному (какому?), а не двум.
Применение метода «пузырька» можно проследить здесь.
Сортировка вставками
Второй метод называется метод вставок ., т.к. на j -ом этапе мы «вставляем» j -ый элемент M[j] в нужную позицию среди элементов M[1] , M[2] ,. . ., M[j-1] , которые уже упорядочены. После этой вставки первые j элементов массива M будут упорядочены.
Сказанное можно записать следующим образом:
Чтобы сделать процесс перемещения элемента M[j] , более простым, полезно воспользоваться барьером: ввести «фиктивный» элемент M[0] , чье значение будет заведомо меньше значения любого из «реальных»элементов массива (как это можно сделать?). Мы обозначим это значение через оо.
Если барьер не использовать, то перед вставкой M[j] , в позицию i-1 надо проверить, не будет ли i=1 . Если нет, тогда сравнить M[j] ( который в этот момент будет находиться в позиции i ) с элементом M[i-1].
Описанный алгоритм имеет следующий вид:
begin M[0] := -oo; for j :=2 to N do begin i := j ; while M[ i ] M[ i — 1] do begin swap (M[ i ],M[ i -1]); i := i -1 end end end; |
Процедура swap нам уже встречалась.
Сортировка посредством выбора
Идея сортировки с помощью выбора не сложнее двух предыдущих. На j -ом этапе выбирается элемент наименьший среди M[j] , M[j+1] ,. . ., M[N] (см. процедуру FindMin ) и меняется местами с элементом M[j] . В результате после j -го этапа все элементы M[j] , M[j+1] ,. . ., M[N] будут упорядочены.
Сказанное можно описать следующим образом:
нц для j от 1 до N-1
выбрать среди M[j] ,. . ., M[N] наименьший элемент и
поменять его местами с M[j]
кц
begin for j :=1 to N -1 do begin FindMin ( j , i ); swap (M[ j ],M[ i ]) end end; |
В программе, как уже было сказано, используется процедура FindMin , вычисляющая индекс lowindex элемента, наименьшего среди элементов массива с индексами не меньше, чем startindex :
procedure FindMin (start index : integer; var lowindex : integer );
var lowelem: . ;
u: integer;
begin
lowindex := start index ;
lowelem := M[startindex];
for u:= start index +1 to N do
if M[u] lowelem then
begin
lowelem := M[u];
lowindex := u
end
end;
Оценивая эффективность применения , учтите что в демонстрации сортировки выбором отсутствует пошаговое выполнение этой процедуры.
Урок 28. Сортировка массива
Урок из серии: «Программирование на языке Паскаль»
Процесс обработки и поиска информации при решении многих задач проходит быстрее и эффективнее, если данные расположены в определенном порядке. Например, различные списки студентов, учащихся, сотрудников — в алфавитном порядке, числовые данные от большего значения к меньшему (или наоборот) и т.д.
Существует довольно много различных методов сортировки массивов, отличающихся друг от друга степенью эффективности, под которой понимается количество сравнений и количество обменов, произведенных в процессе сортировки. Рассмотрим подробно некоторые из них.
Сортировка массива методом простого выбора
При сортировке массива методом выбора применяется базовый алгоритм поиска максимального (минимального) элемента и его номера.
Алгоритм сортировки массива методом выбора:
- Для исходного массива выбрать максимальный элемент.
- Поменять его местами с последним элементом (после этого самый большой элемент будет стоять на своем месте).
- Повторить п.п. 1-2 с оставшимися n-1 элементами, то есть рассмотреть часть массива, начиная с первого элемента до предпоследнего, найти в нем максимальный элемент и поменять его местамис предпоследним (n-1)- м элементом массива, затем с оставшиеся (n-2)-мя элементами и так далее, пока не останется один элемент, уже стоящий на своем месте.
Для упорядочения массива потребуется (n-1) просмотров массива. В процессе сортировки будет увеличиваться отсортированная часть массива, а неотсортированная, соответственно, уменьшаться.
При сортировке данных выполняется обмен содержимого переменных. Для обмена необходимо создавать временную переменную, в которой будет храниться содержимое одной из переменных. В противном случае ее содержимое окажется утерянным.
Задача 1. Массив из 10 элементов отсортировать по возрастанию методом простого перебора.
Напишем процедуру. Входным параметром для неё будет массив. Он же будет и выходным параметром. Поэтому описываем его как параметр-переменная (с ключевым словом var).
В процедуре внешний цикл по i — определяет длину рассматриваемой части массива. Она будет изменяться от n до 2.
Внутренний цикл по j используется для поиска максимального элемента и его номера. В качестве начального значения максимума разумно взять значение последнего элемента рассматриваемой части массива.
Программный код процедуры:
Программный код основной программы:
Процесс упорядочения элементов в массиве по возрастанию методом отбора:
Номер элемента | 1 | 2 | 3 | 4 | 5 |
Исходный массив | 8 | 7 | 5 | 4 | 2 |
Первый просмотр | 2 | 7 | 5 | 4 | 8 |
Второй просмотр | 2 | 4 | 5 | 7 | 8 |
Третий просмотр | 2 | 4 | 5 | 7 | 8 |
Четвертый просмотр | 2 | 4 | 5 | 7 | 8 |
При упорядочивании массива по убыванию необходимо перемещать минимальный элемент. Для чего в алгоритме нахождения максимального элемента достаточно знак «>» поменять на знак « » заменить на «
Сортировка одномерного массива. Метод пузырька.
Сортировка — перестановка местами объектов в определенном порядке. Известно несколько сотен алгоритмов сортировки и их модификаций.
Пусть дана последовательность элементов – A1, А2, …, Аn. Сортировка означает перестановку этих элементов в новом порядке Аk1 , Аk2, …, Akn. Этот порядок соответствует значениям некоторой функции F, такой, что справедливо отношение F(Ak1)
Сортировка простым обменом. Метод пузырька.
Пример 32. Методом пузырька упорядочить (отсортировать) в порядке возрастания массив из 8 целых чисел (44, 55, 12, 42, 94, 18, 06, 67).
Концептуальную модель сортировки рассмотрим на примере восьми целых чисел, которые расположим в первом вертикальном столбце. Вертикальное расположение сортируемого массива наглядно иллюстрирует «всплывание легких элементов (чисел) вверх к поверхности» по мере сортировки массива.
Элементы массива рассматриваются попарно снизу-вверх. Если нижний элемент меньше, то они меняются местами. При первом просмотре (проходе) самый «легкий» элемент оказывается самым верхним. Поэтому при втором просмотре его можно уже не рассматривать. В таблице стрелками показаны перемещения элементов массива после каждого прохода.
Элементы всплывают вверх к поверхности в соответствии с их весами, пока не встретят элемент с более малым весом. С каждым проходом наиболее легкий элемент из оставшихся поднимается на свое место, показанное жирными цифрами. Таким образом, нет необходимости на каждом проходе проверять элементы, стоящие выше выделенных, поскольку они уже отсортированы ранее. Начиная с 6 прохода, состояние массива не изменяется, что объясняется начальной упорядоченностью элементов в исходном массиве.
Эта программа предназначена для изучения сортировки методом пузырька, поэтому взят массив из восьми целых чисел. Массив заранее известен, следовательно, инициализировать Х[1. 8] проще всего в разделе констант. Ввод данных с клавиатуры в этой программе не требуется. Алгоритм программы представляет собой двойной вложенный цикл. Индекс I внешнего арифметического цикла соответствует номеру прохода сортировки. Индекс J это номер элемента в массиве. Для перестановки двух соседних элементов X[J-1] и X[J] местами используется промежуточная рабочая ячейка с идентификатором L. Для качественного обследования используется томография МРТ в Саратове адреса и отзывы указаны на сайте диагностического центра.
PROGRAM PR32;
CONST
X: ARRAY [1.. 8] OF INTEGER = (44, 55,12,42,94,18,06, 67);
VAR
L, I, J: INTEGER;
BEGIN
< Вывод на экран исходного массива X >
FOR I := 1 ТО 8 DO WRITE(X[I]:5);
WRITELN;
FOR I := 2 TO 8
DO FOR J:=8 DOWNTO I
DO IF X[J-1] > X[J]
THEN BEGIN <Переставить X[J-1] с Х[J] местами>
L:=X[J-1];
X[J-1]:=X[j];
X[J]:=L
END;
< Вывод на экран отсортированного массива X >
FOR I := 1 ТО 8 DO WRITE(X[I]:5);
WRITELN
END.