
- •1Введение
- •2Сортировки
- •2.1Сортировки массивов
- •2.2Сортировка простым включением
- •2.3Сортировка простым выбором
- •2.4Сортировка простым обменом
- •2.5Сравнение простых сортировок
- •2.6 Сортировка шелла
- •2.7Пирамидальная сортировка
- •2.8Быстрая сортировка
- •2.9Поиск медианы и квантилей
- •2.10Сравнение сортировок
- •3Поиск подстроки в строке
- •3.1Поиск в строке
- •3.2Простой поиск в строке
- •3.3Поиск в строке. Алгоритм боуера-мура
- •4Генерация перестановок
- •4.1Генерация перестановок последовательности натуральных чисел
- •4.2Генерация перестановок элементов в антилексикографическом порядке
- •4.3Генерация перестановок за одну транспозицию элементов
- •4.4Генерация перестановок за одну транспозицию соседних элементов
- •5Генерация подмножеств
- •5.1Генерация всех подмножеств множества
- •5.2Генерация m -элементных подмножеств множества натуральных чисел
- •Var m: integer; {Размер подмножества}
- •Var I, j, p: integer;
- •5.3Генрация k-компонентных выборок на множестве {0, 1}
- •Var k: integer; {Количество нулей в кортеже}
- •I: integer;
- •Var I, j, p: integer;
- •If Finish then Break {Exit};
- •6Генерация разбиений
- •6.1Разбиение целых чисел
- •Var I, j: integer;
- •Var j: integer;
- •Var I, j, k: integer;
- •Var d, l, Sum: integer;
- •6.2Разбиение множеств
- •/1, 2, 3/ И /4/ затем /1, 2/ и /3, 4/ и т.Д. }
- •I, j, k, r, s: integer;
- •If not Flag2 then
- •If Flag1 then
- •If Forvd[j] then { j движется вперед}
- •7Обходы бинарных деревьев
- •7.1Процедуры прохождения бинарных деревьев
- •8Поиск на бинарных деревьях
- •8.1Процедуры поиска на бинарных деревьях
- •Рекомендованная литература
2.3Сортировка простым выбором
Метод сортировки простым выбором (СПВыб) основан на следующем правиле.
При i, принимающем значения от 1 до n-1, выполняется:
Среди всех элементов аi ,....., аn отыскивается минимальный элемент.
Минимальный элемент меняется местами с элементом аi.
Снова имеем два цикла, но оба типа “for”.
Внешний цикл i=1 до N-1 и внутренний j=i+1 до N.
Процедура имеет следующий вид:
type TArr = array of Item ;
var A: TArr;
procedure StraightsSelection (var A: TArr);
var I, J, K: integer;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
begin
N := Length (A);
for I := 0 to N-2 do
begin
K :=I;
X := A[I];
for J := I+1 to N-1 do
if A[J].Key < X.Key then
begin
K:=J;
X:=A[J];
end;
A[K]:=A[I];
A[I]:=X;
end;
end; {StraightSelection}
begin
{формирование и заполнение массива А}
ShellSort (A); {сортировка массива}
···
end.
Здесь число сравнений ключей не зависит от начального порядка ключей. В результате
С = 1/2(n2 - n).
Анализ показал, что число пересылок:
Mmin = 3(n - 1)
Mmax = tranc(n2/4) + 3(n - 1)
Mavg тяжело определить, несмотря на простоту алгоритма.
В общем случае, СПВыб эффективнее СПВкл, но в случае полностью или значительно отсортированных массивов имеет место обратный порядок.
2.4Сортировка простым обменом
И в предыдущих методах обмен имел место, но в этом случае обмен является основным действием.
Алгоритм сортировки простым обменом (СПО) основан на принципе сравнения и обмена пары соседних элементов. Здесь, как и ранее, совершаются простые проходы по массиву, каждый раз просеивая наименьший элемент оставшегося массива, двигаясь от правого к левому концу массива.
Этот метод получил название метод пузырька. Это очевидно если располагать массивы вертикально.
начальн. состоян. |
i=2 |
i=3 |
i=4 |
i=5 |
i=6 |
i=7 |
i=8 |
4 |
01 |
01 |
01 |
01 |
01 |
01 |
01 |
55 |
44 |
11 |
11 |
11 |
11 |
11 |
11 |
11 |
55 |
44 |
22 |
22 |
22 |
22 |
22 |
33 |
11 |
55 |
44 |
33 |
33 |
33 |
33 |
99 |
33 |
33 |
55 |
44 |
44 |
44 |
44 |
22 |
99 |
99 |
33 |
55 |
55 |
55 |
55 |
01 |
22 |
22 |
99 |
99 |
99 |
99 |
77 |
77 |
77 |
77 |
77 |
77 |
77 |
77 |
99 |
Процедура СПО, соответствующая приведенному примеру, строиться по схеме перемещения наименьшего элемента на самую левую позицию при каждой очередной итерации по переменной I. Она имеет вид:
type TArr = array of Item ;
var A: TArr;
procedure BubbleSortMin (var A: TArr);
var I, J: integer;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
begin
N := Length (A);
for I := 1 to N-1 do
begin
for J := N-1 downto I do
if A[J-1].Key > A[J].Key then
begin
X:=A[J-1] ;
A[J-1]:=A[J] ;
A[J]:=X;
end;
end;
end; {BubbleSortMin}
begin
{формирование и заполнение массива А}
ShellSort (A); {сортировка массива}
···
end.
Аналогично процедура СПО может строиться по схеме перемещения наибольшего элемента на самую правую позицию при каждой очередной итерации по переменной I. Она имеет вид:
procedure BubbleSortMax (var A: TArr);
var I, J: integer;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
begin
N := Length (A);
for I := 1 to N-1 do
begin
for J:=0 downto N - I do
if A[J].Key > A[J+1].Key then
begin
X:=A[J+1];
A[J+1]:=A[J];
A[J]:=X;
end;
end;
end; {BubbleSortMax}
В алгоритме "пузырька":
С=1/2(n2-n)
Mmin = 0
Mmax = 1/2(n2-n)
Mavg = 3/4(n2-n)
По своим показателям этот алгоритм уступает двум предыдущим из-за большого числа перестановок. Но он легко оптимизируется.
Первый шаг в оптимизации вытекает из рассмотренного примера. Так, видно что, три последних прохода являются лишними, поскольку элементы уже рассортированы. Поэтому очевидным способом улучшить данный алгоритм является фиксация факта осуществления на данном проходе хоть одного обмена. Если факт отрицателен, то алгоритм может закончить работу. Это приводит к отказу от последующих итераций по индексу I.
procedure BubbleSortMax1 (var A: TArr);
var I, J: integer;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
F: boolean;
begin
N := Length (A);
for I := 0 to N-2 do
begin
F := false;
for J := 0 to N-I do
if A[J].Key > A[J+1] .Key then
begin
X := A[J+1];
A[J+1] := A[J];
A[J] := X;
F := true;
end;
if not F then Exit;
end;
end; {BubbleSortMax1}
Другой вариант реализации этой же процедуры представляется более компактным. Здесь совмещены особенности оператора и контроль на отсутствие перестановки элементов.
procedure BubbleSortMax11 (var A: TArr);
var I, J: integer;
Changed: Boolean;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
repeat
Changed := false;
N := Length (A);
for i := 0 to N-1 do
begin
if A[i].Key > A[i+1].Key then
begin
{обменяем i-ый и i+1-ый элементы}
X := A[i];
A[i] := A[i+1];
A[i+1] := X;
Changed := true;
end;
end;
until not Changed;
end; {BubbleSortMax11}
Второй шаг в оптимизации состоит в том, чтобы запоминать не только сам факт обмена, но и место (индекс k) последнего обмена. Очевидно, что все пары элементов с индексами, меньше чем отмеченный, уже расположены в нужном порядке. Поэтому все последующие проходы можно заканчивать на этом индексе вместо того, чтобы двигаться до устанавливаемой для каждой итерации нижней границы i.
Таким образом, на каждой итерации нижняя граница будет смещаться не на 1 , как следует из оператора цикла, а, в общем случае, на величину, значительно больше 1. Это автоматически приводит к отказу от последующих итераций по индексу I.
Например, пусть имеем такую последовательность ключей:
01, 11, 22, 77, 55, 44, 99, 33
Тогда процесс сортировки будет иметь следующий вид:
-
начальное состояние
i=2; k=2
i=3; k=5
i=4; k=6
i=5; k=6
01
01
01
01
01
11
11
11
11
11
22
22
22
22
22
7
7
33
33
33
33
55
77
44
44
44
44
55
77
55
55
99
44
55
77
77
33
99
99
99
99
Таким образом, вместо 7-ми итераций потребовалось только 4-ре, причем последняя итерация - только для определения признака “перестановок не было”.
procedure BubbleSortMin1 (var A: TArr);
var I, J, K, L: integer;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
begin
N := Length (A);
K := N-1;
for I := 0 to N-2 do
begin
L := 0;
for J := N-1 downto K do
if A[J-1].Key > A[J].Key then
begin
X:=A[J-1] ;
A[J-1]:=A[J] ;
A[J]:=X;
L := J;
end;
if L = 0 then Exit;
K := L;
end;
end; {BubbleSortMin1}
Здесь можно заметить интересную асимметрию. Пусть имеем такую последовательность.
начальн. состоян. |
i=2 |
i=3 |
i=4 |
i=5 |
i=6 |
i=7 |
i=8 |
9 |
01 |
01 |
01 |
01 |
01 |
01 |
01 |
11 |
99 |
11 |
11 |
11 |
11 |
11 |
11 |
22 |
11 |
99 |
22 |
22 |
22 |
22 |
22 |
33 |
22 |
22 |
99 |
33 |
33 |
33 |
33 |
44 |
33 |
33 |
33 |
99 |
44 |
44 |
44 |
55 |
44 |
44 |
44 |
44 |
99 |
55 |
55 |
77 |
55 |
55 |
55 |
55 |
55 |
99 |
77 |
01 |
77 |
77 |
77 |
77 |
77 |
77 |
99 |
Из примера видно, что:
малый элемент 01 “пузырек”, расположенный изначально в конце последовательности, среди потенциально больших элементов, всплывает на свое место за один проход;
большой элемент 99 “камешек”, расположенный изначально в начале последовательности среди потенциально меньших элементов опускается на свое место только на один шаг на каждом проходе.
Эта асимметрия вызвана особенностью алгоритма, когда перестановки выполняются в сортируемой последовательности от конечного элемента к начальному.
Поэтому возникает идея выполнять поочередно в одном проходе перестановки от начального элемента к конечному элементу, а во втором проходе от конечного к начальному элементам сортируемой последовательности. Такой алгоритм назвали шейкер-сортировкой.
l=2 l=3 l=3 l=4 l=4
r=8 r=8 r=7 r =7 r =4
44 01 01 01 01
55 44 44 11 11
11 55 11 44 22
33 11 33 22 33
99 33 55 33 44
22 99 22 55 55
01 22 77 77 77
77 77 99 99 99
Процедура шейкер - сортировки имеет вид:
procedure ShakerSoft (var A: TArr);
var J, K, L, R : integer;
N: integer; {количество элементов сортируемой последовательности}
X: Item;
begin
L := 1;
N := Length (A);
R := N-1;
K := N;
repeat
{определяется нижняя, согласно рисунка, граница неотсортированной последовательности}
for J:= R downto L do
{погружение “камешка”}
if A[J-1].Key > A[J].Key then
begin
X := A[J-1];
A[J-1] := A[J];
A[J] := X;
K := J; {запоминается место последней перестановки}
end;
L := K + 1;
for J := L to R do
{всплытие "пузырька"}
if A[J-1].Key > A[J].Key then
begin
X := A[J-1];
A[J-1] := A[J];
A[J] := X;
K := J; {запоминается место последней перестановки}
end;
R := K - 1;
{определяется верхняя, согласно рисунка, граница неотсортированной последовательности}
until L > R;
end; {ShakerSoft}
Но даже модифицированный алгоритм СПО всё равно уступает алгоритмам СПВст и СПВкл. Собственно шейкер-сортировку выгодно использовать в тех случаях, когда известно, что элементы уже почти упорядочены, что встречается крайне редко.
Все простые методы сортировки, особенно СПВст и СПО в принципе, перемещают каждый элемент на одну позицию на каждом элементарном шаге. Поэтому все они требуют порядка n2 шагов.
Следовательно, любое улучшение алгоритмов сортировки должно основываться на принципе пересылки элементов за один цикл на расстоянии больше чем 1-на позиция.
Анализ показал, что в среднем каждый элемент во время сортировки перемещается на n/3 мест. Это число даёт ключ к поиску более совершенных методов сортировки.
Далее будут рассмотрены три усовершенствованных метода сортировки - по одному для каждого рассмотренного простого метода сортировки.