| Россия |
Сортировка
Первое действие (взятие элемента из первого отрезка) может производиться при одновременном выполнении двух условий:
(1) первый отрезок не кончился (
);
(2) второй отрезок кончился (
) или не
кончился, но элемент в нем не меньше очередного элемента
первого отрезка [
)
и (
)].
Аналогично для второго действия. Итак, получаем
p0 := p; q0 := q; s0 := p;
while (p0 <> q) or (q0 <> r) do begin
| if (p0 < q) and ((q0 = r) or ((q0 < r) and
| |(x[p0+1] <= x[q0+1]))) then begin
| | b [s0+1] := x [p0+1];
| | p0 := p0+1;
| | s0 := s0+1;
| end else begin
| | {(q0 < r) and ((p0 = q) or ((p0<q) and
| | (x[p0+1] >= x[q0+1])))}
| | b [s0+1] := x [q0+1];
| | q0 := q0 + 1;
| | s0 := s0 + 1;
| end;
end;(Если оба отрезка не кончены и первые невыбранные элементы в них равны, то допустимы оба действия; в программе выбрано первое.)
Остается лишь переписать результат слияния обратно в массив x. (Предупреждение. Если обратное копирование выполняется вне процедуры слияния, то не забудьте про последний отрезок.)
Программа имеет привычный дефект: обращение к несуществующим элементам массива при вычислении булевских выражений.
Решение 2 (сортировка деревом).
Нарисуем "полное двоичное дерево" - картинку, в которой снизу один кружок, из него выходят стрелки в два других, из каждого - в два других и так далее
Будем говорить, что стрелки ведут "от отцов к сыновьям": у каждого кружка два сына и один отец (если кружок не в самом верху или низу). Предположим для простоты, что количество подлежащих сортировке чисел есть степень двойки, и они могут заполнить один из рядов целиком. Запишем их туда. Затем заполним часть дерева под ними по правилу:

Изымем из сортируемого массива минимальный элемент. Для
этого его надо вначале найти. Это можно сделать, идя от
корня: от отца переходим к тому сыну, где записано то же
число. Изъяв минимальный элемент, заменим его символом
и скорректируем более низкие ярусы (для этого
надо снова пройти путь к корню). При этом считаем, что
. Тогда в корне появится второй по
величине элемент, мы изымаем его, заменяя бесконечностью
и корректируя дерево. Так постепенно мы изымем все элементы
в порядке возрастания, пока в корне не останется
бесконечность.
При записи этого алгоритма полезно нумеровать кружки
числами
- при этом сыновьями кружка
номер n являются кружки 2n и
.
Подробное изложение этого алгоритма мы опустим, поскольку
мы изложим более эффективный вариант, не требующий
дополнительной памяти, кроме конечного числа переменных
(в дополнение к сортируемому массиву).
Мы будем записывать сортируемые числа во всех вершинах
дерева, а не только на верхнем уровне. Пусть
- массив, подлежащий сортировке.
Вершинами дерева будут числа от 1 до n ; о числе x[i] мы будем говорить как о числе, стоящем
в вершине i. В процессе сортировки количество вершин
дерева будет сокращаться. Число вершин текущего дерева
будем хранить в переменной k. Таким образом, в процессе
работы алгоритма массив
делится на
две части: в
хранятся числа на
дереве, а в
хранится уже
отсортированная в порядке возрастания часть массива -
элементы, уже занявшие свое законное место
На каждом шаге алгоритм будет изымать максимальный элемент дерева и помещать его в отсортированную часть, на освободившееся в результате сокращения дерева место.
Договоримся о терминологии. Вершинами дерева считаются
числа от 1 до текущего значения переменной k.
У каждой вершины s могут быть сыновья 2s
и 2s+1. Если оба этих числа больше k, то сыновей
нет; такая вершина называется листом. Если
, то вершина s имеет ровно одного сына
( 2s ).
Для каждого s из
рассмотрим
"поддерево" с корнем в s: оно содержит
вершину s и всех ее потомков (сыновей, внуков и так
далее - до тех пор, пока мы не выйдем из отрезка
). Вершину s будем называть регулярной, если стоящее в ней число -
максимальный элемент s -поддерева; s -поддерево
назовем регулярным, если все его вершины
регулярны. (В частности, любой лист образует регулярное одноэлементное поддерево.)
Заметим, что истинность утверждения " s -поддерево регулярно" зависит не только от s, но от текущего значения k.
Схема алгоритма такова:
k:= n
... Сделать 1-поддерево регулярным;
{x[1],..,x[k] <= x[k+1] <=..<= x[n]; 1-поддерево регулярно,
в частности, x[1] - максимальный элемент среди x[1]..x[k]}
while k <> 1 do begin
| ... обменять местами x[1] и x[k];
| k := k - 1;
| {x[1]..x[k-1] <= x[k] <=...<= x[n]; 1-поддерево
| регулярно везде, кроме, возможно, самого корня }
| ... восстановить регулярность 1-поддерева всюду
end;В качестве вспомогательной процедуры нам понадобится процедура восстановления регулярности s -поддерева в корне. Вот она:
{s-поддерево регулярно везде, кроме, возможно, корня}
t := s;
{s-поддерево регулярно везде, кроме, возможно, вершины t}
while ((2*t+1 <= k) and (x[2*t+1] > x[t])) or
| ((2*t <= k) and (x[2*t] > x[t])) do begin
| if (2*t+1 <= k) and (x[2*t+1] >= x[2*t]) then begin
| | ... обменять x[t] и x[2*t+1];
| | t := 2*t + 1;
| end else begin
| | ... обменять x[t] и x[2*t];
| | t := 2*t;
| end;
end;
