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

информатика_2 / Программирование_С

.pdf
Скачиваний:
85
Добавлен:
16.03.2016
Размер:
2.31 Mб
Скачать

3.1.2.. Сортировки

Общие замечания: здесь мы придерживаемся классической схемы [8,9] — сортировки производятся на массиве и, как правило, «на месте», то есть вспомогательные массивы не используются. Сортируются данные целого типа и, как правило, по возрастанию. Размерность массива обозначается через n.

3.1.2.1.Сортировки включениями — или вставками

Сортировка вставками — общее описание алгоритма:

Элементы просматриваются по одному, и каждый новый элемент вставляется

вподходящее место среди ранее упорядоченных элементов.

3.1.2.1.1.Простые включения

Пусть записи a[1],...,a[i–1] уже размещены в "выходной" части массива; берут элемент a[i] и переносят в выходную часть, вставляя его в подходящее место.

Пример:

Исходный массив: 24 31 15 20 52 6

I=1

24

31

15

20

52

6

 

 

┌┘

 

 

 

 

 

 

i=2

24

31

 

15

 

20

52

 

6

 

┌────┘

 

 

 

 

i=3

15

24

 

31

 

20

52

 

6

 

 

┌───┘

 

 

 

i=4

15

20

 

24

 

31

52

 

6

 

 

 

 

 

 

 

┌┘

 

i=5

15

20

 

24

 

31

52

 

6

 

┌──────────┘

i=6

6

15

20

24

31

52

Результат:

6 15 20 24 31 52

Псевдокод для этого алгоритма выглядит так:

{

for (i = 2 to n)

{

//вставляемый_элемент = mas[i]

//вставить вставляемый_элемент

//на подходящее место в mas[1]…mas[i]

};

};

181

Теперь нужно вставить элемент:

//начинаем с j == i – 1 и сравниваем mas[j] с in_el = mas[i]

//если in_el < mas[j], то переносим mas[j] направо и продвигаемся

//налево, иначе — вставляем in_el

Более детально,

{

for (i = 2 to n)

{

in_el = mas[i]; j = i – 1;

while ( (in_el < mas[j]) and (j >= 1) )

{

mas[j + 1] = mas[j]; j = j – 1

};

mas[j + 1] = in_el;

};

}

3.1.2.1.2. Бинарные включения Этот метод представляет собой улучшение метода простых включений,

который имеет по крайней мере два недостатка:

необходимость перемещения данных, причем при вставке элементов, близких к концу массива, приходится перемещать почти весь массив,

необходимость поиска места для вставки, на что также тратится много ресурсов.

Первый недостаток органично присущ методу вставок и неискореним. Второй может быть в значительной степени устранен при использовании бинарного поиска места для вставки. Идея бинарного поиска проста: часть массива, в которой нужно произвести поиск, делим пополам и сравниваем искомое число со средним по положению элементом (рис. 3.4). Если искомый элемент больше

Рис. 3.4. Бинарный поиск

182

среднего, следующий поиск проводим в правом подмассиве, в противном случае в левом.

Здесь low и high — текущие индексы границ подмассива, l_new и h_new — новые индексы, in_l — вставляемый элемент, m — индекс среднего элемента.

Алгоритм поиска:

1)in_el < a[m — далее ищем слева, high = h_new

2)in_el >= a[m — далее ищем справа, low = l_new

Иллюстрация на псевдокоде:

{

for (i = 2 to n)

{

in_el = mas[i]; low = 1;

high = i;

while (low < high)

{

mid = (low + high) /2; if (in_el < mas[mid]) high = mid – 1;

else

low = mid + 1;

};

for (j = i–1 down to low) mas[j + 1] = mas[j];

mas[low] = in_el;

};

};

3.1.2.1.3. Задачи на сортировки вставками

3.1.2.1.3.1. Простые вставки

Напишите программу, реализующую метод простых вставок.

3.1.2.1.3.2. Простые вставки с бинарными включениями

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

183

3.1.2.1.3.3. Сортировка Шелла

Это — усовершенствование сортировки с помощью прямого включения. Сам метод объясняется и демонстрируется на примере. Сначала отдельно группируются и сортируются элементы, отстоящие друг от друга на расстоянии 4. Такой процесс называется четверной сортировкой. В нашем примере восемь элементов и каждая группа состоит из двух элементов. После первого прохода элементы перегруппировываются — теперь каждый элемент группы отстоит от другого на две позиции — и вновь сортируются. Это называется двойной сортировкой. И наконец, на третьем проходе идет обычная или одинарная сортировка.

Пример.

Исходный массив и пары для четверной сортировки: 25 57 48 37 12 92 86 33

└──────────┘

└──────────┘

└──────────┘

//четверная сортировка дает (шаг 5) 25 57 33 37 12 92 86 48,

//двойная сортировка дает (шаг 3) 25 12 33 37 48 92 86 57,

//одинарная сортировка дает (шаг 1) 12 25 33 47 58 57 86 92,

//то есть отсортированный массив.

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

Такой метод в результате дает упорядоченный массив, и каждый проход от предыдущих только выигрывает (так как каждая i-сортировка объединяет две группы, уже отсортированные 2i-сортировкой). Так же очевидно, что расстояния в группах можно уменьшать по-разному, лишь бы последнее было единичным, ведь в самом плохом случае последний проход и сделает всю работу. Однако совсем не очевидно, что такой прием «уменьшающихся расстояний» может дать лучшие результаты, если расстояния не будут степенями двойки. Поэтому приводимый псевдокод программы не ориентирован на некую определенную последовательность расстояний. Все t расстояний обозначаются соответственно

h1 , h2 ,..., ht , для них выполняются условия

ht =1, hi+1 < hi

Каждая h-сортировка программируется как сортировка с помощью прямого включения. Причем простота условия окончания поиска места для включения обеспечивается методом барьеров.

Иллюстрация на псевдокоде:

# define t 4

184

int i,j,k,s; int x, m;

int h[3] = {9,5,3,1}; for (m=1 to t)

{

k:= h[m];

 

s = – k;

// место барьера

for (i=k+1 to n)

{

x=a[i]; j=i – k;

if (s == 0) s= -k;

s=s + 1; a[s]=x;

while (x < a[j])

{

a[j + k] = a[j]; =j – k;

}

a[j + k]=x;

}

}

Ясно, что каждая из сортировок нуждается в постановке своего собственного барьера и программу для определения его местоположения необходимо делать насколько возможно простой. Поэтому приходится расширять массив a не только

на одну-единственную компоненту a0 , а на h1 компонент. Его описание

выглядит так: #define SIZE h1 + n int a [SIZE];

Напишите программу, реализующую данный метод.

3.1.2.1.3.4. Сортировка с вычислением адреса

Ксортировкам вставками относится также сортировка с вычислением адреса.

Вэтом методе строится набор списков, количество которых равно количеству элементов массива. Затем к каждому элементу массива применяется некоторая функция f. Результат этой функции определяет, в какой из списков будет помещена данная запись. Эта функция должна иметь то свойство, что если х<у, то f(x)<f(y). Такая функция называется функцией, сохраняющей порядок. Все записи в одном списке будут иметь ключи, которые меньше или равны ключам записей в другом списке. Элемент помещается в список в правильной последовательности при помощи использования любого метода сортировки,

185

например сортировки простыми вставками. После того как все элементы первоначального массива будут помещены в списки, эти списки могут быть соединены для того, чтобы получить отсортированный результат.

Пример.

Исходный массив:

25 57 48 37 12 92 86 33

Создадим 10 списков, один для каждой из 10 возможных первых цифр числа (система десятичная!). Первоначально каждый из этих списков пуст. Списки начинаются с указателей, хранящихся в дополнительном массиве f. Функция f(i) возвращает первую цифру текущего элемента массива и таким образом указывает на первый элемент в списке, чья первая цифра равна i. Например, после просмотра первого элемента (25) он помещается в список с указателем f(2). Каждый из списков строится как отсортированный. После обработки всех элементов из первоначального массива списки выглядят так:

f(0)

NULL

 

 

f(1)

12

NULL

 

f(2)

25

NULL

 

f(3)

33

37

NULL

f(4)

48

NULL

 

f(5)

57

NULL

 

f(6)

NULL

 

 

f(7)

NULL

 

 

f(8)

86

NULL

 

f(9)

92

NULL

 

Остается объединить списки или же перенести данные обратно в исходный массив.

Напишите программу, реализующую данный метод.

3.1.2.1.3.5. Сортировка двухпутевыми вставками

Эта сортировка является некоторой модификацией сортировки простыми вставками в следующем виде. Создается некоторый выходной массив размером n. Элемент x[1] помещается в средний элемент этого массива. Когда непрерывная группа элементов находится в этом массиве, место для нового элемента отводится при помощи сдвига всех элементов с меньшими значениями на один шаг влево или элементов с большими значениями на один шаг вправо. Выбор, в какую сторону выполнять сдвиг, зависит от того, какой из них приведет к меньшему количеству сдвигов.

Напишите программу, реализующую этот метод.

3.1.2.1.3.6. Сортировка слиянием вставок

Эта сортировка выполняется следующим образом.

Шаг 1: для всех нечетных i от 1 до n – 1 сравниваем x[i] и x[iI + 1]. Помещаем большее из них в следующую позицию некоторого массива large, а меньшее в следующую позицию некоторого массива small. Если n нечетно, то помещаем

186

х[n] в последнюю позицию массива small. (Массив large имеет размер int(n/2), массив small — размер int(n/2) или int(n/2) + 1 в зависимости от того, является ли n четным или нечетным.)

Шаг 2: выполняем сортировку массива large, используя рекурсивно сортировку слиянием вставок. Когда некоторый элемент large[j] перемещается в large[k], элемент small[j] также перемещается в small[k]. В конце этого шага large[i]≤ large [i+1] для всех i, меньших int(n/2), и small[i] ≤large[i] для всех i,

меньших или равных int(n/2).]

Шаг 3: скопируем элемент small[1] и все элементы массива large в элементы от х[1] до x[int(n/2) + 1].

Шаг 4: определим целое число num(i) как (2i+1+(–1)i)/ 3. Начиная с i=1 и продвигаясь с шагом 1, пока выполняется num(i) ≤int(n/2) + 1, вставляем по очереди элементы от small[num(i + 1)] до small[num(i) + 1] в массив x, используя метод бинарных вставок. Например, если n=20, то последовательные значения num следующие: num(1) = 1, num(2)=3, num(3)=5, num(4) = 11, что равно int(n/2) + 1. Так, элементы массива small вставляются в следующем порядке: small[3], small[2]; затем small[5], small[4]; затем small[10], small[9], small[8], small[7], small[6]. В этом примере нет элемента small[11].]

Напишите программу, чтобы реализовать этот метод.

3.1.2.1.3.7. Модификация быстрой сортировки

Модифицируйте быструю сортировку так, чтобы она использовала сортировку простыми вставками, когда размер подмассива меньше некоторого числа s.

Напишите программу, реализующую данный метод.

3.1.2.2. Сортировки выбором

3.1.2.2.1. Простой выбор Выбирается элемент с наименьшим значением; он меняется местами с первым;

процесс повторяется для оставшихся n–1 элементов. Пример:

Исходный массив: 24 31 15 20 52 6

┌──────────────┐

исх. 24 31 15 20 52 6

└──────────────┘

┌──┐

i = 2 6 31 15 20 52 24

└──┘

┌──┐

i = 3 6 15 31 20 52 24

└──┘

┌────┐ i = 4 6 15 20 31 52 24

└────┘

187

┌──┐ i = 5 6 15 20 24 52 31

└──┘

Результат:

6 15 20 24 31 52

Псевдокод для этого алгоритма выглядит так:

{

for (i = 1 to n – 1)

{

//найти индекс k наименьшего элемента из a[i],...,a[n]

//поменять местами a[i] и a[k]

};

};

Более детально,

{

for (i = 1 to n–1)

{

ind_min = i; a_min = a[i];

for (j = i + 1 to n) if a[j] < a_min

{

ind_min = j; a_min = a[j];

};

a[ind_min] = a[i]; a[i] = a_min;

};

};

Заметим, что число сравнений здесь не зависит от порядка элементов (в отличие от других сортировок)

188

3.1.2.2.2. Пирамидальная сортировка Термин «дерево» в этом разделе не имеет ничего общего с деревьями

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

Строим дерево снизу (внизу — исходный массив), попарно выбирая наименьший элемент:

6

15 6

24 15 6

24

31

15

20

52 6

.

В таком дереве информация дублируется, поэтому используем специальный вид двоичного дерева — пирамиду.

Это двоичное дерево такое, что :

a[i] <= a[ 2*i ] a[i] <= a[ 2*i +1 ]

отсюда следует, что a[1] — минимальный элемент пирамиды.

Сначала расположим исходный массив в пирамиде:

a[1]

a[2] a[3]

a[4] a[5] a[6] a[7]

а затем пересортируем.

I. Построение пирамиды

Описание алгоритма:

1)отделяем правую часть дерева, начиная с n/2 + 1 (нижний уровень дерева);

2)берем элемент левее этой части массива и "просеиваем" его сквозь пирамиду по пути, где находятся меньшие его элементы, которые одновременно поднимаются вверх; из двух возможных путей выбирается путь через меньший элемент.

Пример.

Исходный массив: 24 31 15 20 52 6

189

Отделение правой части:

(6/2 +1) = 4

Располагаем исходный массив в виде пирамиды, начиная с первого элемента — он верхний; первым просеиваемым элементом будет 15, этот факт обозначим символом *. Ниже показаны три шага процесса, причем на каждом шаге позиция просеиваемого элемента сдвигается на один шаг влево, к началу

массива:

 

 

 

 

 

 

24

 

 

24

 

 

31

15*

31*

6

20

52

6

20

52

15

 

24*

 

 

6

 

 

20

6

20

 

15

31

52

15

31

52

24

Этот массив еще не упорядочен, однако процедура просеивания является основой для пирамидальной сортировки. Заметим, что в итоге просеивания наименьший элемент массива оказался на вершине пирамиды, а условие (предок- меньше-любого-потомка) соблюдено.

Псевдокод для алгоритма просеивания элемента с индексом left сквозь часть пирамиды left + 1, right выглядит так:

{

//текущий_индекс = left

//буфер = текущий_элемент

while (// индекс_потомка <= right)

{

// выбрать_меньшего_потомка

if ( //буфер <= потомок)

//прекратить_движение

//переместить_потомка_на_место_предка

//перейти_к_потомкам

};

};

Более детально, просеивание для элемента с индексом left выполняется так:

{

190

Соседние файлы в папке информатика_2