<< Пред. стр. 8 (из 19) След. >>
returnПаскаль:
Без барьера:
for i:= 2 to n do
begin
x:= a(i);
for j:= i - 1downto 1 do
if x < a(j ) then
a(j +1):= a(j )
else goto 1;
end;
end;
1: a(j + 1):= x;
end;
end;
С барьером:
for i := 2 to n do
begin
x:= a(i);
a(0):= x; {a(0) - барьер}
j:= i - 1;
while x < a(j ) do
begin
a(j +1):= a(j );
j:=j - 1;
end;
a(j +1):= x;
end;
Эффективность алгоритма
Число сравнений ключей Ci при i- м просеивании самое большее равно i-1, самое меньшее - 1; если предположить, что все перестановки из N ключей равновероятны, то среднее число сравнений = i/2. Число же пересылок Mi=Ci+3 (включая барьер). Минимальные оценки встречаются в случае уже упорядоченной исходной последовательности элементов, наихудшие же оценки - когда они первоначально расположены в обратном порядке. В некотором смысле сортировка с помощью включения демонстрирует истинно естественное поведение. Ясно, что приведенный алгоритм описывает процесс устойчивой сортировки: порядок элементов с равными ключами при нем остается неизменным.
Количество сравнений в худшем случае, когда массив отсортирован противоположным образом, Сmax = n(n - 1)/2, т. е. - О (n2). Количество перестановок Mmax = Cmax + 3(n-1), т.е. - О (n2). Если же массив уже отсортирован, то число сравнений и перестановок минимально: Cmin = n-1; Mmin = =3(n-1).
6.2 Сортировка методом прямого выбора
Этот прием основан на следующих принципах:
1. Выбирается элемент с наименьшим ключом.
2. Он меняется местами с первым элементом a 1.
3. Затем этот процесс повторяется с оставшимися n-1 элементами, n-2 элементами и т.д. до тех пор, пока не останется один, самый "большой" элемент.
Алгоритм формулируется так:
for i = 1 to n - 1
x = a(i)
k = i
for j = i + 1 to n
if a(j) < x then
k = j
x = a(k)
endif
next j
a(k) = a(i)
a(i) = x
next i
return for i := 1 to n - 1 do
begin
x := a[i];
k := i;
for j := i + 1 to n do
if a[j] < x then
begin
k := j;
x := a[k];
end;
a[k] := a[i];
a[i] := x;
end;
Эффективность алгоритма:
Число сравнений ключей C, очевидно, не зависит от начального порядка ключей. Можно сказать, что в этом смысле поведение этого метода менее естественно, чем поведение прямого включения. Для C при любом расположении ключей имеем:
C = n(n-1)/2
Порядок числа сравнений, таким образом, О(n2)
Число перестановок минимально Мmin = 3(n - 1) в случае изначально упорядоченных ключей и максимально, Мmax = 3(n - 1) + С, т.е. порядок О(n2), если первоначально ключи располагались в обратном порядке.
В худшем случае сортировка прямым выбором дает порядок n2, как для числа сравнений, так и для числа перемещений.
6.3. Сортировка с помощью прямого обмена (пузырьковая сортировка)
Классификация методов сортировки редко бывает осмысленной. Оба разбиравшихся до этого метода можно тоже рассматривать как "обменные" сортировки. В данном же, однако, разделе описан метод, где обмен местами двух элементов представляет собой характернейшую особенность процесса. Изложенный ниже алгоритм прямого обмена основывается на сравнении и смене мест для пары соседних элементов и продолжении этого процесса до тех пор, пока не будут упорядочены все элементы.
Как и в упоминавшемся методе прямого выбора, мы повторяем проходы по массиву, сдвигая каждый раз наименьший элемент оставшейся последовательности к левому концу массива. Если мы будем рассматривать массивы как вертикальные, а не горизонтальные построения, то элементы можно интерпретировать как пузырьки в чане с водой, причем вес каждого соответствует его ключу. В этом случае при каждом проходе один пузырек как бы поднимается до уровня, соответствующего его весу (см. рис.6.3).
Такой метод широко известен под именем "пузырьковая сортировка". В своем простейшем виде он представлен ниже.
Программы на псевдокоде и Паскале:
for i = 2 to n
for j = n to i step -1
if a(j) < a(j - 1) then
x = a(j - 1)
a(j - 1) = a(j)
a(j) = x
endif
next j
next i
return for i := 2 to n do
for j := n downto i do
if a[j] < a[j - 1]then
begin
x := a[j - 1];
a[j - 1] := a[j];
a[j] := x;
end;
end;
end; В нашем случае получился один проход “вхолостую”. Чтобы лишний раз не переставлять элементы, можно ввести флажок fl, который остается в значении false , если при очередном проходе не будет произведено ни одного обмена. На нижеприведенном алгоритме добавления отмечены курсивом.
fl = true
for i = 2 to n
if fl = false then return
endif
fl = false
for j = n to i step -1
if a(j) < a(j - 1) then
fl = true
x = a(j - 1)
a(j - 1) = a(j)
a(j) = x
endif
next j
next i
return
Улучшением пузырькового метода является шейкерная сортировка, где после каждого прохода меняют направление во внутреннем цикле.
Эффективность алгоритма:
Число сравнений Cmax = n(n-1)/2 , О(n2).
Число перемещений Мmax=3Cmax=3n(n-1)/2, О(n2).
Если массив уже отсортирован и применяется алгоритм с флажком, то достаточно всего одного прохода, и тогда получаем минимальное число сравнений
Cmin = n - 1, О(n),
а перемещения вообще отсутствуют
Сравнительный анализ прямых сортировок показывает, что обменная "сортировка" в классическом виде представляет собой нечто среднее между сортировками с помощью включений и с помощью выбора. Если же в нее внесены приведенные выше усовершенствования, то для достаточно упорядоченных массивов пузырьковая сортировка даже имеет преимущество.
6.4. Улучшенные методы сортировки
6.4.1. Быстрая сортировка (Quick Sort)
Относится к методам обменной сортировки. В основе лежит методика разделения ключей по отношению к выбранному.
Слева от 6 располагают все ключи с меньшими, а справа - с большими или равными 6 (рис. 6.4).
Sub Sort (L, R)
i = L
j = R
x = a((L + R) div 2)
repeat
while a(i) < x do
i = i + 1
endwhile
while a(j) > x do
j = j - 1
endwhile
if i <= j then
y = a(i)
a(i) = a(j)
a(j) = y
i = i + 1
j = j - 1
endif
until i > j
if L < j then
sort (L, j)
endif
if i < R then
sort (i, R)
endif
return
sub QuickSort
sort (1, n)
return procedure Sort (L, R: integer);
begin
i := L;
j := r;
x := a[(L + r) div 2];
repeat
while a[i] < x do
i := i + 1;
while a[j] > x do
j := j - 1;
if i <= j then
begin
y := a[i];
a[i] := a[j];
a[j] := y;
i := i + 1;
j := j - 1
end;
until i > j;
if L < j then sort (L, j);
if i < r then sort (i, r);
end;
procedure QuickSort;
begin
sort (1, n);
end;
Эффективность алгоритма:
О(n log n) - самый эффективный метод.
6.4.2 Сортировка Шелла (сортировка с уменьшающимся шагом)
В 1959 году Д. Шеллом было предложено усовершенствование сортировки с помощью метода прямого включения. Работа его представлена на рис. 6.5:
Сначала отдельно группируются и сортируются элементы, отстоящие друг от друга на расстоянии 4. Такой процесс называется четверной сортировкой. После первого прохода элементы перегруппировываются - теперь каждый элемент группы отстоит от другого на 2 позиции - и вновь сортируются. Это называется двойной сортировкой. И, наконец, на третьем проходе идет обычная или одиночная сортировка.
На первый взгляд можно засомневаться: если необходимо несколько процессов сортировки, причем в каждый включаются все элементы, то не добавят ли они больше работы, чем сэкономят? Однако, на каждом этапе либо сортируется относительно мало элементов, либо элементы уже довольно хорошо упорядочены и требуют сравнительно немного перестановок.
Ясно, что такой метод в результате дает упорядоченный массив, и, конечно, сразу же видно, что каждый проход от предыдущих только выигрывает; также очевидно, что расстояния в группах можно уменьшать по разному, лишь бы последнее было единичным, ведь в самом плохом случае последний проход и сделает всю работу.
Приводимый алгоритм основан на методе прямой вставки без барьера и не ориентирована на некую определенную последовательность расстояний, хотя в нем для конкретности заданы шаги 5, 3 и 1.
При использовании метода барьера каждая из сортировок нуждается в постановке своего собственного барьера, и программу для определения его местонахождения необходимо делать насколько возможно простой. Поэтому приходится расширять массив до [-h1..N].
h[1..t] - массив размеров шагов
a[1..n] - сортируемый массив
k - шаг сортировки
x - значение вставляемого элемента
Subroutine ShellSort
Псевдокод:
const t = 3
h(1) = 5
h(2) = 3
h(3) = 1
for m = 1 to t
k = h(m)
for i = k + 1 to n
x = a(i)
for j = i - k to 1 step -k
if x < a(j) then
a( j+k) = a(j)
else
goto L
endif
next j
L: a(j+k) = x
next i
next m
return
Паскаль:
const t = 3;
h(1) = 5;
h(2) = 3;
h(3) = 1;
var
h: array [1..t] of integer;
a: array [1..n] of integer;
k, x, m, t, i, j: integer;
begin
for m = 1 to t do
begin
k:= h(m);
for i = k + 1 to n do
begin
x:= a(i);
for j = i-k downto k do
begin
if x < a(j ) then
a(j+k):= a(j);
else
goto 1;
end ;
end;
end;
1: a(j+1) = x ;
end;
end .
Не доказано, какие расстояния дают наилучший результат, но они не должны быть множителями один другого.
Д. Кнут предлагает такую последовательность шагов (в обратном порядке): 1, 3, 7, 15, 31, … То есть: h m-1 = h m + 1,
t = log2n - 1. При такой организации алгоритма его эффективность имеет порядок O ( n1.2)
Контрольные вопросы
1. Что такое сортировка?
2. Назовите основные методы сортировки.
3. Какие методы сортировки относятся к строгим?
4. Какие методы сортировки относятся к улучшенным?
5. Какая сортировка называется устойчивой?
6. В чем состоит суть метода прямого включения?
7. В чем состоит суть метода прямого выбора?
8. В чем состоит суть метода прямого обмена?
9. Назовите разницу между этими тремя методами.
10. Какой метод сортировки является самым эффективным?
11. К какому из основных методов относится метод Шелла?
7. ПРЕОБРАЗОВАНИЕ КЛЮЧЕЙ (РАССТАНОВКА)
Метод расстановок (хеширования) направлен на быстрое решение задачи о местоположении элемента в структуре данных. В методе расстановок данные организованы как обычный массив. Поэтому Н - отображение, преобразующее ключи в индексы массива, отсюда и появилось название «преобразование ключей», обычно даваемое этому методу. Следует заметить, что нет никакой нужды обращаться к каким-либо процедурам динамического размещения, ведь массив — это один из основных, статических объектов. Метод преобразования ключей часто используется для таких задач, где можно с успехом воспользоваться и деревьями.