Россия, Пошатово |
Сортировка
Первое действие (взятие элемента из первого отрезка) может производиться при одновременном выполнении двух условий:
(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;