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

Алгоритмы сортировки массивов

.docx
Скачиваний:
21
Добавлен:
06.02.2015
Размер:
19.38 Кб
Скачать

Алгоритмы сортировки массивов

Задача сортировки массива, то есть перестановки элементов массива так, чтобы они были упорядочены по возрастанию, убыванию или другой аналогичной характеристике, является одной из основных технических задач программирования. С этой задачей мы сталкиваемся и при записи фамилий учеников в классном журнале, и при подведении итогов соревнований, и даже при упорядочении игральных карт, например, при игре в преферанс. Очень часто сортировка используется как первый шаг в алгоритме решения более сложной задачи. Рассмотрим наиболее простые алгоритмы сортировки массивов и подсчитаем их вычислительную сложность (см. “Теория алгоритмов”).

“Пузырьковая” сортировка

Традиционно наиболее простой в реализации считается так называемая “пузырьковая” сортировка. Суть ее в случае упорядочения по неубыванию заключается в следующем. Будем просматривать слева направо все пары соседних элементов: a1 и a2, a2 и a3, …, an-1 и an. Если при этом ai > ai+1, то элементы меняем местами. В результате такого просмотра массива максимальный элемент окажется на крайнем справа (своем) месте. Об остальных элементах ничего определенного сказать невозможно. Будем просматривать массив снова, исключив из рассмотрения правый элемент. На своем месте теперь окажется уже второй по величине элемент. И так далее. В последнем просмотре будут участвовать только первый и второй элементы. Общее число просмотров при этом равно N–1. Приведем фрагмент программы, реализующий описанный алгоритм:

for j := 1 to n — 1 do

{цикл по просмотрам}

for i := 1 to n — j do

{просмотр массива}

if a[i] > a[i + 1] then

begin

x := a[i];

a[i] := a[i + 1];

a[i + 1] := x

end;

При оценке вычислительной сложности алгоритмов сортировки обычно отдельно подсчитывают количество операций сравнения и количество операций присваивания, т.к. заранее неизвестно, какая из них окажется более трудоемкой, ведь сравниваться между собой могут не только числа, но и строки, и другие достаточно сложные объекты. Иногда обе операции сопоставимы по трудоемкости.

Количество сравнений в данном алгоритме равно

N – 1 + N – 2 + N – 3 + ... + 1 = N(N – 1)/2.

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

3(N – 1 + N – 2 + N – 3 + ... + 1) = 3N(N – 1)/2.

Говорят, что данный алгоритм имеет квадратичную сложность как по числу сравнений, так и по числу присваиваний. “Пузырьковым” он называется потому, что в результате каждого просмотра максимальный из оставшихся элементов оказывается на своем месте — “всплывает”. По-другому такая сортировка называется обменной.

Сортировка “прямым выбором”

Рассмотрим алгоритм сортировки, основанный на принципиально иной идее. Найдем минимальный элемент в массиве и поменяем его с первым элементом массива. В результате он окажется на своем месте. Затем найдем минимальный элемент среди оставшихся и поменяем его со вторым элементом. На N–1-м шаге мы закончим упорядочивание нашего массива. Такой алгоритм называется сортировкой прямым выбором. Приведем фрагмент программы, реализующий описанный алгоритм:

for i := 1 to n — 1 do

begin

mini := i;

for j := i + 1 to n do

{ищем минимальный элемент}

if a[j] < a[mini] then mini := j

{меняем минимальный элемент с i-м}

x := a[i];

a[i] := a[mini];

a[mini] := x

end;

Количество выполняемых операций в данном алгоритме всегда одинаково. Для сравнений оно равно

N – 1 + N – 2 + N – 3 + ... + 1 = N(N – 1)/2,

а для присваиваний — всего 3(N – 1).

Сортировка “подсчетом”

То есть данный алгоритм квадратичный по числу сравнений и линейный (эффективный) по числу присваиваний.

Пусть нам надо отсортировать массив, состоящий из десятичных цифр. Оказывается, что в этом случае существует гораздо более эффективный алгоритм сортировки по сравнению с рассмотренными выше. А именно, подсчитаем во вспомогательном массиве d количество каждой из цифр. Сделать это можно за один проход массива a. Затем заполним массив a заново согласно значениям массива d:

const maxn = 10000;

var d : array[0..9] of integer;

{массив для подсчета}

a : array[1..maxn] of 0..9;{массив цифр}

n, i, j, k : integer;

begin

readln(n); {n — количество цифр}

for i := 1 to n do a[i] := random(10);

for j := 0 to 9 do d[j] := 0;

for i := 1 to n do {подсчет каждой цифры}

d[a[i]] := d[a[i]] + 1;

k := 0;

for j := 0 to 9 do {заполняем a заново}

for i := 1 to d[j] do

begin

k := k + 1;

a[k] := j

end

end.

Заметим, что эта программа работает верно и в случае, когда какой-либо из цифр во вводимой последовательности нет совсем. Подобный алгоритм называют сортировкой подсчетом. Его вычислительная сложность в общем случае составляет 2N + 2m операций, где m — количество различных значений среди элементов в массиве (в нашем примере их было всего 10). Очевидно, что если m Ј N и мы можем отвести память для подсчета количества каждого из m возможных значений для элементов массива, то алгоритм будет иметь линейную сложность относительно N.