Добавлен: 15.11.2018
Просмотров: 5597
Скачиваний: 36
Метод "пузырька" предполагает следующую физическую модель преобразования массива данных.
Из N чисел массива всплывает (как в бокале с шампанским) "пузырек"- число, оказывающееся больше в случае сортировки по возрастанию, чем (N - 1) чисел. Затем всплывает "пузырек" - число, оказывающееся больше чем (N - 2) оставшихся, но меньше, чем первый всплывший "пузырек" и т.д.
Рассмотрим массив из 6 элементов, содержащих следующие значения:
1 3 4 2 6 5.
Пройдем по массиву, сравнивая первый элемент со вторым, второй с третьим, и так далее до пятого. Будем проверять упорядоченность и, если она нарушена, т.е. если значение некоторого элемента больше значения следующего элемента, то поменяем эти значения местами. Например, дойдя до третьего элемента, значение которого равно 4, мы обнаружим, что оно больше значения четвертого элемента, равного 2. Выполнив обмен, получим новую последовательность значений:
1 3 2 4 6 5.
После этого перейдем к четвертому элементу. Теперь его значение равно 4. Значение следующего элемента равно 6, оно больше 4, поэтому обмена значениями не происходит. Перейдя к пятому, обнаруживаем, что его значение больше значения шестого, и производим обмен. Итак, выполнив один проход по массиву, получим последовательность значений:
1 3 2 4 5 6.
Эта последовательность до конца не отсортирована. Однако после первого прохода можем с уверенностью сказать, что наибольшее значение находится на своем месте - на правой границе массива. Выполнив еще один проход по первым пяти элементам массива, получим последовательность значений:
1 2 3 4 5 6
После этого прохода предпоследнее по величине значение 5 стало на свое место. Выполняя новые проходы, каждый раз сокращая количество просматриваемых элементов на 1, мы в конечном счете отсортируем все значения. Этот алгоритм можно реализовать с помощью двух циклов for - внешнего и внутреннего. Число повторений внешнего цикла будет на 1 меньше числа элементов массива (так как последовательность из одного значения проверять нет смысла). Число повторений внутреннего цикла будет последовательно сокращаться от N-1 до 1:
program sort;
{Программа сортировки одномерного массива в порядке возрастания
методом обмена пар ("пузырьковая" сортировка)}
const
n = 12;
var
а : array[1..n] of integer;
x, i, j : integer;
begin
{ввод массива}
for i:=1 to n do
begin
write( ‘Введите ‘, i,’-e значение’);
readln(a[i]);
end;
{сортировка}
for i:=1 to n-1 do
for j:=1 to n-i do
if a[j]>a[j+1] then
begin
{перестановка элементов массива}
x:=a[j];
a[j]:=a[j+1];
a[j+1]:=x;
end;
{вывод отсортированных значений массива}
for i:=1 to n do
write(a[i]:5);
readln; end.
Этот алгоритм нередко называют "пузырьковой" сортировкой, так как значения элементов как бы "всплывают" на свои места.
Алгоритм, сформулированный выше, — это, строго говоря, лишь один из алгоритмов сортировки обменом. Есть и другие алгоритмы, которые естественно отнести к алгоритмам сортировки обменом. Приведем пример такого алгоритма. Последовательными просмотрами чисел x1, ..., xn найти наименьшее i такое, что xi > xi+1. Поменять xi и xi+1 местами и возобновить просмотр с начала массива. Когда не удается найти i, массив будет упорядочен.
Другой вариант алгоритма сортировки обменом. Предположим, что k - 1 элементов массива отсортированы. Последовательным просмотром чисел xk, ..., xn найти такое число, что xk > xi, и поменять местами xk и xi. В результате такого просмотра чисел xk, ..., xn на k-м месте окажется нужное число.
Сортировка выбором
Сортировку выбором называют еще сортировкой поиском последовательных минимумов.
Рассмотрим один из алгоритмов сортировки выбором.
Очевидно, что первое место в массиве должен занять наименьший элемент, второе — наименьший из всех остальных элементов и т. д. Пусть x1,…, xi-1 уже получили нужные значения. Тогда определение индекса k наименьшего элемента из хi, xi+1, ... , xп и перестановка xi с хk приведут к тому, что х1, ..., xi будут иметь нужные значения. Таким образом, схема программы решения поставленной задачи может быть следующей:
program vibor;
описания;
begin
ввести массив х;
for i:=1 to n do
begin
найти индекс k наименьшего элемента среди хi, xi+1 ,.., xn;
переставить xi с хk ;
writeln(xi)
end
end.
Как только элемент занял свое место, он сразу же выводится.
Приступим к детализации схемы. Ввод массива не доставляет затруднений:
for i:=1 to n do read(x[i])
и можно перейти к поиску индекса k наименьшего элемента среди x[i], ..., х[п]:
k:= i;
for j:= i+1 to n do
if x[j] < x[k] then k:= j
Для перестановки x[i] с x[k] привлекаем дополнительную переменную v: v:=x[i]; x[i]:= x[k]; x[k]:=v.
Теперь можно написать программу (считаем элементы массива действительными числами):
program vibor;
const n=20;
type vect=array[1. .n] of real;
var x : vect; v : real;
i, j, k : integer;
begin
for i:=1 to n do read(x[i]);
for i:=1 to n do
begin
k:= i;
for j:= i+1 to n do
if x[j] < x[k] then k:= j;
v:=x[i]; x[i]:= x[k]; x[k]:=v
writeln(x[i])
end
end.
При решении практических задач упорядочивание x1, ..., xn, как правило, сопровождается некоторыми дополнительными действиями. Например, если x1, y1, x2, y2,..., xп, yп—это значения аргумента х и некоторой функции y=f(x), то перестановка x1,..., хn должна сопровождаться перестановкой y1, ..., yп. Элементы y1, ..., yn переставляются так же, как x1, ...,xп вне зависимости от значений самих y1 ,.., yп. Рассмотрим эту задачу, переставленные значения x1, y1, ..., xп, yп будут выведены в два столбца:
x1 y1
x2 y2
. . . . . . .
xп yп
Программа:
program tabl;
const n =20;
type vect=array[1. .n] of real;
var x, y : vect;
v : real;
i, j, k : integer;
begin
for i:=1 to n do read(x[i], y[i]);
for i:=1 to n do
begin
k:= i;
for j:=i+1 to n do
if x[j] < x[k] then k:= j;
v:=x[i]; x[i]:= x[k]; x[k]:=v;
v:=y[i]; y[i]:=y[k]; y[k]:=v;
writeln(x [i], y[i])
end
end.
Ниже приведен пример программы, в которой определяется максимальный из оставшихся элементов. Программа, реализующая этот алгоритм для сортировки по убыванию, выглядит следующим образом:
program sort1;
{Программа сортирует значения одномерного массива в порядке убывания методом выбора максимума)
const
n = 20;
var
а : array[ 1..n] of integer;
i, j, max, i_max : integer;
begin
{ввод значений элементов массива}
for i:=1 to n do
read(a[i]);
{сортировка}
for i:=1 to n-1 do
begin
max:=a[i]; {поиск максимального значения элемента}
i_max:=i; {среди значений элементов от}
for j:=i+1 to n do {i-гo до n-гo}
if a[j] > max then
begin
max:=a[j];
i_max:=j;
end;
a[i_max]:=a[i]; {обмен i-ro и максимального элементов}
a[i]:=max;
end;
{вывод отсортированного массива}
for i:=1 to n do
write(a[i]:7);
end.
Сортировка вставкой
Для упорядочивания массива попарно различных чисел x1, ..., xп может быть использован алгоритм сортировки простыми вставками. Опишем этот алгоритм применительно к упорядочиванию по возрастанию.
Массив данных разделяется на две части: отсортированную и неотсортированную. Элементы из неотсортированной части поочередно выбираются и вставляются в отсортированную часть так, чтобы не нарушить в ней упорядоченность элементов. В начале работы алгоритма в качестве отсортированной части принимается первый элемент массива.
Таким образом, будет иметь место (n - 1) проходов (где п - размерность массива), каждый из которых будет включать четыре действия:
- взятие очередного элемента из неотсортированной части и сохранение в дополнительной переменной;
- поиск позиции в отсортированной части массива для вставки элемента, взятого предыдущим действием, который бы не нарушил порядок;
- сдвиг элементов массива для вставки элемента массива в отсортированную часть;
- вставка элемента в найденную позицию.
Для реализации данного метода существует несколько алгоритмов, которые отличаются способом поиска позиции вставки.
Ниже приведена программа, реализующая один из алгоритмов сортировки простыми вставками.
{Сортировка вставкой}
Program SortInsetr ;
Uses Crt;
Const n=5;
Var a :array [1..n] of byte;
b, i , j , k, p, с : byte ;
begin
ClrScr ;
Randomize; {Инициализация генератора случайных чисел} WriteLn('Исходный массив:');
{Генерация и печать исходного массива}
for i:=1 to n do
begin
a[i]:=Random(100) ;
Write(a[i]:3)
end;
{Внешний цикл сортировки}
for i:=2 to n do
begin
b:=a[i];
{Взятие элемента массива из неотсортированной части и сохранение его в дополнительной переменной}
j:=1; {Присвоение значения индексной переменной}
{Цикл поиска позиции вставки}
while b>a[j] do
j:=j+l; {Фиксация позиции вставки}
{Цикл сдвига элементов массива для вставки}
for k:= i-1 downto j do
a[k+1]:=a[k] ;
{Вставка элемента массива в найденную позицию}
а[j]:=b;
end;
WriteLn('Отсортированный массив: ') ;
{Цикл печати отсортированного массива}
for i:=1 to n do Write(a[i]:3) ;
ReadKey
end.
Один из возможных результатов работы программы выглядит следующим образом:
Сортировка методом вставки
Исходный массив:
22 45 6 24 4
Отсортированный массив:
4 6 22 24 45
Алгоритм сортировки простыми вставками можно изменить следующим образом. Место, на которое надо вставить xi в уже упорядоченную совокупность x1, ..., xi-1, определяется алгоритмом деления пополам (см. алгоритм поиска места элемента в п. 5.2). Получается новый алгоритм сортировки, который называется алгоритмом сортировки бинарными вставками (слова «бинарная вставка» следует понимать как «вставка делением пополам»).
По числу сравнений алгоритм сортировки бинарными вставками намного лучше, чем рассмотренные выше алгоритмы. Различными авторами предпринимались попытки доказательства оптимальности алгоритма сортировки бинарными вставками и даже печатно сообщалось о якобы найденном доказательстве. Позднее выяснилось, что и этот алгоритм не оптимален, так как он требует восьми сравнений для упорядочивания пяти чисел, а на самом деле достаточно семи сравнений.
Алгоритм быстрой сортировки
Алгоритм быстрой сортировки заключается в следующем. Отсортируем последовательность значений одномерного массива по возрастанию. Для этого возьмем некоторый элемент последовательности (назовем его контрольным). Пусть это будет, например, последний элемент. Далее разделим всю последовательность на две группы: в первую поместим те, которые предшествуют контрольному элементу, а во вторую - те, которые должны следовать за ним. Для этого будем сравнивать с контрольным элементы последовательности, начиная с первого, и до тех пор, пока не дойдем до элемента, значение которого больше контрольного. Запомним координату этого элемента в переменной After. Далее, двигаясь от правого конца последовательности, будем сравнивать элементы с контрольным до тех пор, пока не дойдем до элемента, меньшего контрольного или до элемента с индексом After. Запомним индекс элемента, на котором закончились сравнения, в переменной Before. Совпадение значений Before и After означает, что все элементы, стоящие до After, меньше контрольного, а элементы с номером After и после него - больше. Поменяв местами элементы After и контрольный, получим правильно разбитую на две части последовательность: в первой, слева от контрольного, стоят элементы, меньше его, а во второй, справа, - элементы, больше контрольного. Если переменные After и Before не совпадают, то это означает, что элемент с индексом After должен стоять после контрольного, а элемент с индексом Before - перед контрольным. Поменяем местами элементы Before и After и будем повторять процедуру нахождения элементов Before и After до тех пор, пока они не совпадут, т.е. пока не представится возможность получить последовательность, правильно разбитую на две группы.
Итак, мы разбили последовательность таким образом, что элементы, не превышающие контрольный, стоят слева от него, а остальные - справа. Следовательно, контрольный элемент находится на том месте, на котором он должен стоять в отсортированной последовательности. Далее, если в двух последовательностях, на которые мы разбили исходную, содержится более одного элемента, можно и их разбить на две группы, поставив контрольные элементы этих последовательностей на свои места. И так будем продолжать до тех пор, пока последовательность не будет отсортирована. Возможный вариант программы с процедурой быстрой сортировки может выглядеть, например, следующим образом:
Program Sort_Q;
Type
Vector=Array [1..20] of Byte;
Var
I, N : Byte;
Vec : Vector;
Procedure Quick_Sort(var A:Vector; First, Last : Byte);
{Процедура быстрой сортировки массива.
Входные параметры:
1) A типа Vector - сортируемый одномерный массив;
2) First, Last типа Byte - индексы, указывающие
начальный и конечный элементы сортируемой последовательности}
Var
After, Before : Byte;
Control, Work : Integer;
Begin
{обрабатываем массив, если он содержит не менее 2 элементов}
If First < Last Then
Begin
Control:=A[Last];
After:=First;
Before:=Last;
{разбиение массива на 2 группы}
Repeat
{поиск индекса первого элемента, большего или равного контрольному, After}
While A[After]<Control Do
Inc(After);
{поиск индекса элемента, меньшего контрольного во второй половине массива, Before}
While (A[Before] >= Control) And (Before > After) Do
Dec(Before);
{если во второй половине массива такой элемент найден, поменять его местами с элементом After)
If After < Before Then
Begin
Work:=A[Before];
A[Before]:=A[After];
A[After]:=Work;
End;
Until After=Before;
A[Last]:=A[After];
A[After]:=Control;
{массив разбит правильным образом:
слева от контрольного элемента находятся элементы, предшествующие ему, а справа - следующие за ним}
Quick_Sort(A, First, After-1); {обработать левую подгруппу}
Quick_Sort(A, After+1, Last); {обработать правую подгруппу}
End;
End;
Begin {Main}
Write(‘Введите размерность массива (не более20)’);
Readln(N);
For I:=1 To N Do
Begin
Write(‘Введите А[‘,I:2,’]= ‘);
Readln(Vec[I]);
End;
Quick_Sort(Vec, 1, N);
Writeln(‘Отсотированный массив’);
For I:=1 To N Do
Write (Vec[I]:4);
Writeln; End.
Процедура Quick_Sort является рекурсивной, так как в ней имеется обращение самой к себе. Рекурсия упрощает реализацию алгоритма быстрой сортировки. Этот способ сортировки является более эффективным, чем рассмотренные нами прямые способы сортировки, хотя и не так прост для понимания.
Рассмотрим, как будет осуществляться сортировка массива из 6 элементов при выполнении программы Sort_Q. В основной программе после операторов, предназначенных для ввода с клавиатуры значений элементов исходного массива, происходит вызов процедуры быстрой сортировки Quick_Sort. При этом обрабатываемой последовательностью элементов будет весь массив А, так как индекс начального элемента равен 1, индекс конечного элемента - N (N=6). При каждом обращении к процедуре Quick_Sort программа переходит на следующий уровень или возвращается на предыдущий, в зависимости от выполнения условия: индекс начального элемента (First) меньше индекса конечного элемента (Before), т.е. First < Before. Так как условие First < Before выполняется, то программа переходит на уровень 1.
Ниже приведены изменения исходного массива (3 1 5 6 2 4) на различных уровнях обработки. Элементы After и Before помечены соответственно символами а и b, a контрольный элемент - символом с.
Уровень 1. Последовательность 3 1 5 6 2 4
a bc
3 1 5 6 2 4
a b с