Сортировка
4.1. Квадратичные алгоритмы
4.1.1. Пусть - целые числа. Требуется построить массив , содержащий те же числа, для которого .
Замечание. Среди чисел могут быть равные. Требуется, чтобы каждое целое число входило в столько же раз, сколько и в .
Решение. Удобно считать, что числа и представляют собой начальное и конечное значения массива x. Требование " a и b содержат одни и те же числа" будет заведомо выполнено, если в процессе работы мы ограничимся перестановками элементов x.
k := 0; {k наименьших элементов массива установлены на свои места} while k <> n do begin | s := k + 1; t := k + 1; | {x[s] - наименьший среди x[k+1]...x[t] } | while t<>n do begin | | t := t + 1; | | if x[t] < x[s] then begin | | | s := t; | | end; | end; | {x[s] - наименьший среди x[k+1]..x[n] } | ... переставить x[s] и x[k+1]; | k := k + 1; end;
4.1.2. Дать другое решение задачи сортировки, использующее инвариант "первые k элементов упорядочены" ( ).
Решение.
k:=1; {первые k элементов упорядочены} while k <> n do begin | t := k+1; | {k+1-ый элемент продвигается к началу, пока не займет | надлежащего места, t - его текущий номер} | while (t > 1) and (x[t] < x[t-1]) do begin | | ...поменять x[t-1] и x[t]; | | t := t - 1; | end; end;
Замечание. Дефект программы: при ложном выражении (t>1) проверка требует несуществующего значения x[0].
Оба предложенных решения требуют числа действий, пропорционального . Существуют более эффективные алгоритмы.
4.2. Алгоритмы порядка n log n
4.2.1. Предложить алгоритм сортировки за время (число операций при сортировке элементов не больше для некоторого и для всех ).
Мы предложим два решения.
Решение 1 (сортировка слиянием).
Пусть k - положительное целое число. Разобьем массив на отрезки длины k. (Первый - , затем и так далее.) Последний отрезок будет неполным, если n не делится на k. Назовем массив k-упорядоченным, если каждый из этих отрезков в отдельности упорядочен. Любой массив 1-упорядочен. Если массив k-упорядочен и , то он упорядочен.
Мы опишем, как преобразовать k-упорядоченный массив в 2k-упорядоченный (из тех же элементов). С помощью этого преобразования алгоритм записывается так:
k:=1; {массив x является k-упорядоченным} while k < n do begin | ...преобразовать k-упорядоченный массив в 2k-упорядоченный; | k := 2 * k; end;
Требуемое преобразование состоит в том,что мы многократно "сливаем" два упорядоченных отрезка длины не больше k в один упорядоченный отрезок. Пусть процедура
при сливает отрезки и в упорядоченный отрезок (не затрагивая других частей массива x ).Тогда преобразование k -упорядоченного массива в 2k -упорядоченный осуществляется так:
t:=0; {t кратно 2k или t = n, x[1]..x[t] является 2k-упорядоченным; остаток массива x не изменился} while t + k < n do begin | p := t; | q := t+k; | r := min (t+2*k, n); | {min(a,b) - минимум из a и b} | слияние (p,q,r); | t := r; end;
Слияние требует вспомогательного массива для записи результатов слияния - обозначим его b. Через p0 и q0 обозначим номера последних элементов участков, подвергшихся слиянию, s0 - последний записанный в массив b элемент. На каждом шаге слияния производится одно из двух действий:
b[s0+1]:=x[p0+1]; p0:=p0+1; s0:=s0+1;
или
b[s0+1]:=x[q0+1]; q0:=q0+1; s0:=s0+1;
(Любители языка C написали бы в этом случае b[++s0]=x[++p0] и b[++s0]=x[++q0].)