Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Рацеев С.М. Программирование на языке Си.pdf
Скачиваний:
366
Добавлен:
23.03.2016
Размер:
1.65 Mб
Скачать

Приложение 2. АЛГОРИТМЫ СОРТИРОВКИ

Задача – отсортировать некоторый массив a размером n по возрастанию.

Несколько слов о сложности алгоритмов

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

O(log2n), O(n), O(n·log2n), O(n2).

Для наглядности приблизительно вычислим время выполнения работы алгоритма на конкретном компьютере в зависимости от функции сложности. Рассмотрим достаточно производительный компьютер с процессором i5-3450 с частотой 3100 МГц и оперативной памятью DDR3 16 Гб 1600 МГц. Работать будем с n- элементыми массива. На таком компьютере, например, инициализация 1 000 000 000 элементного массива длится около 2 секунд времени. Поэтому будем считать, что обработка происходит со скоростью 500 000 000 элементов массива в секунду.

Пусть сложность алгоритма является квадратичной: W(n)= n22 .

Применим

такой алгоритм к массиву из n элементов. Если

n=1 000 000, то алгоритм будет выполняться приблизительно

10000002

= 1000 секунд ≈ 16 минут.

2 500000000

 

301

Но если массив состоит из n=1 000 000 000 элементов, то данный алгоритм будет работать приблизительно

10000000002

= 1000000000 секунд ≈ 11574 суток ≈ 31 год.

2 500000000

 

 

 

Пусть теперь сложность алгоритма представлена такой функ-

цией: W(n)=

n log2 n

и массив состоит из того же миллиарда эле-

 

2

 

 

 

ментов. Тогда данный алгоритм будет работать приблизительно

1000000000 log21000000000 =30 секунд.

2 500000000

Пусть массив состоит из n=1 000 000 000 элементов. В следующей таблице приведена зависимость реального времени работы алгоритма от его сложности.

 

 

 

 

 

сложность алго-

O(log2n)

O(n)

O(n·log2n)

O(n2)

ритма

 

 

 

 

время работы

микросекунды

секунды

минуты

годы

1. Метод прямого выбора

Данный метод основан на следующем принципе. На i-м шаге (i = 0, 1, …, n-2) ищется минимальный элемент из всех элементов массива с индексами i, i+1, …, n-1, после чего этот минимальный элемент меняется местами с i-м элементом массива, и переходим к следующему шагу.

Сложность сортировки в любом случае: O(n2).

void SelectionSort(double *a, const int n)

 

{

 

 

int i,

/* номер текущего шага

*/

j,

/* счетчик

*/

k;

/* индекс минимального элемента

*/

double min;

/* значение минимального элемента */

for(i = 0; i < n-1; i++)

{

k = i; /*начальное значение индекса минимального элемента*/ min = a[i]; /* начальное значение минимального элемента */

302

for(j = i + 1; j < n; j++) if (a[j] < min)

{

k = j;

min = a[j];

}

a[k] = a[i]; a[i] = min;

}

}

На основе метода прямого выбора можно решать, например, следующие задачи.

Перемешивание элементов в массиве. Пусть с массивом требуется выполнить операцию, противоположную сортировке, а именно перетасовать (перемешать) все значения в исходном массиве a. В этом случае можно применить алгоритм, немного похожий на метод прямого выбора: на i шаге (i = 0, 1, …, n-2) произвольным образом выбирается элемент из всех элементов с индексами i, i+1, …, n-1, после чего этот элемент меняется местами с i элементом массива.

void InterMix(double *a, int n)

{

int i, k; double buf;

for(i = 0; i < n-1; i++)

{

k = i + rand()%(n - i);

buf = a[i]; a[i] = a[k]; a[k] = buf;

}

}

2. Метод прямого включения

303

Основная идея сортировки методом прямого включения состоит в том, что при добавлении нового элемента в уже отсортированный массив этот элемент ставится на нужную позицию так, чтобы получившийся массив снова был отсортированным. Пусть i- 1 первых элементов (i ≥ 1) исходного массива уже отсортированы. Нам требуется передвинуть все из данных i-1 элементов, большие i-го элемента, на одну позицию вправо. На освободившееся место ставим данный i-й элемент. После чего мы уже имеем i отсортированных первых элементов.

Первый элемент массива a является одноэлементным отсортированным массивом. Поэтому последовательно рассматриваем случаи i = 1, 2, …, n-1.

Сложность сортировки в среднем случае: O(n2).

void InsertSort(double *a, int n)

{

int i, j; double buf;

for (i = 1; i < n; i++)

{

buf = a[i];

for(j = i - 1; j >= 0 && a[j] > buf; j--) a[j+1] = a[j];

a[j+1] = buf;

}

}

Нахождения нескольких минимальных (максимальных) эле-

ментов. Рассмотрим еще один алгоритм нахождения нескольких минимальных элементов одномерного массива (см. пример 9 параграф 5.2) на основе сортировки вставками.

void Min(int *a, int n, int *min, int m)

{

int i, j; min[0] = a[0];

for(i = 1; i < m; i++)

304