СОРТИРОВКА И ПОИСК

advertisement
Алгоритмы сортировки и поиска
Задача поиска. Задан массив A из n элементов и некоторое значение p, (поисковое). Требуется найти такой номер i, что A[i]=p.
Результат поиска:
1) существует единственный элемент с номером i, для которого A[i]=p;
2) A[i]≠p при любых i=1,2,...,n;
3) существует несколько элементов с номерами
i1,i2,... таких, что A[i1]=p,A[i2]=p,...
___________________________________________
Алгоритм поиска одного элемента в неупорядоченном массиве:
___________________________________________
i:=0; k:=1;
while k<=n do
if A[k]=p then
begin i:=k; k:=n+1 end
else k:=k+1;
__________________________________________
Трудоемкость в наихудшем: O(n).
Дихотомический поиск в
упорядоченном массиве
Область подозрительных элементов:
A[b], A[b+1], ...,A[e].
Вначале: b=1,e=n.
Сравнение поискового значения p с элементом
A[c], где c=(b+e)div 2. Возможны два
исхода (если существует искомый элемент):
1) A[c]<p. Искомый элемент среди:
A[c+1],...,A[e];
2) A[c]≥p. Искомый элемент среди:
A[b],...,A[c].
В 1-м случае: b:=c+1, в 2-м: e:=c.
__________________________________________
b:=1; e:=n;
{1}
while b<e do
{2}
begin
{3}
c:=(b+e)div 2;
{4}
if A[c]<p then b:=c+1
{5}
else e:=c
{6}
end;
{7}
if A[b]=p then i:=b
{8}
else i:=0
{9}
__________________________________________
Предусловие: b:=1, e:=n.
Постусловие: b=e.
Инвариант: Искомый элемент
(если он существует) находится среди:
A[b], ...,A[e].
Трудоемкость алгоритма.
Пусть:
2m – 1 < k ≤ 2m ,
(*)
где k – размер подозрительной области
Вначале k = n.
При четном k размеры области уменьшаются
ровно в два раза, а при нечетном – в два с
округлением, неравенство (*) сохраняется.
После m = log 2 n шагов: k = 1.
Общее количество сравнений C в наихудшем
случае:
C  log 2 n  1.
_________________________________________
В упорядоченном массиве элементы, равные
поисковому значению, располагаются подряд.
Программой ищется номер начального элемента
такой группы.
Чтобы отыскать номер последнего элемента в
этой группе, программу следует изменить:
c:=(b+e+1)div 2;
{4}
if A[c]<=p then b:=c
{5}
else e:=c-1
{6}
__________________________________________
Поиск в списках
1. Поиск значения S0 путем последовательного
просмотра в линейном списке:
_________________________________________
p0:=p;
while (p0<>nil)and(p0^.s<>S0) do
p0:=p0^.p
_________________________________________
Инвариант цикла (указатель p0 указывает на i-й
элемент):
«среди элементов списка с номерами 1, …, i – 1
содержимое ни одного из них не равно S0.»
_________________________________________
2. Поиск значения S0 в упорядоченном по
возрастанию списке:
p0:=p;
while (p0<>nil)and(p0^.s<S0) do
p0:=p0^.p;
if (p0<>nil)and(p0^.s>S0) then
p0:=nil
_________________________________
Задачи с упорядоченными (полностью
или частично) массивами
1. Удаление повторяющихся значений элементов
в упорядоченном массиве:
__________________________________________
m:=1; C[1]:=A[1];
for i:=2 to n do
if C[m]<A[i] then
begin m:=m+1; C[m]:=A[i] end
Инвариант: среди элементов {C[1],…,C[m]}
имеются все, которые есть среди
{A[1],…,A[i-1]}, но без повторений.
__________________________________________
2. В заданном числовом массиве A из n элементов требуется определить начало и конец упорядоченного отрезка, имеющего максимальную
длину:
b:=1; bm:=1; em:=1;
for i:=1 to n do
if (i=n)or(A[i]>A[i+1]) then
begin
{обнаружен конец очередного отрезка}
if (i-b)>(em-bm) then
begin bm:=b; em:=i end;
b:=i+1
end
___________________________________
Алгоритмы сортировки
Простейший алгоритм сортировки:
i:=1;
while i<n do
if X[i]<=X[i+1]
then i:=i+1
else begin
z:=X[i]; X[i]:=X[i+1];
X[i+1]:=z;
i:=1
end
__________________________________
Инвариант:
{X[1] ≤ X[2] ≤ . . . ≤ X[i]},
«Набор значений в массиве X остается неизменным»
Инверсия: если X[i]>X[j]} , при i<j.
Максимальное количество инверсий
(в наихудшем):
если j=2, то 1,
если j=3, то 2,
. . .
если j=n, то n – 1.
Всего:
1 + 2 + . . . + (n – 1) = n·(n – 1)/2.
Пусть:
{X[1]≤X[2]≤ ... ≤X[i]}, X[i]>X[i+1]
Чтобы устранить одну инверсию, потребуется i
шагов цикла.
Чтобы устранить i инверсий в начале массива,
если было: X[1]≥X[i+1] ,
чтобы после этого выполнялось:
{X[1]≤X[2]≤ ... ≤X[i]≤X[i+1]},
потребуется
i+(i-1)+...+1 = i*(i+1)/2
шагов цикла.
Т.о., чтобы устранить все инверсии, потребуется
1·(1+1)/2 + 2·(2+1)/2 + . . . + (n–1)·n/2 ≈ n3/6.
Т.е. трудоемкость алгоритма в наихудшем имеет
порядок O(n3).
Трудоемкость в наилучшем – O(n).
_________________________________________
Улучшение простейшего алгоритма сортировки
i:=1;
while i<n do
if X[i]<=X[i+1]
then i:=i+1
else begin
z:=X[i]; X[i]:=X[i+1];
X[i+1]:=z;
if i>1 then i:=i-1
end
_________________________________________
Трудоемкость.
Если на каком-либо этапе работы алгоритма
оказалось, что:
X[1]<=X[2]<=...<= X[i], X[i]>X[i+1],
то, чтобы для элементов в начале массива
выполнялись соотношения
X[1]<=X[2]<=...<= X[i+1],
в наихудшем случае цикл должен выполниться
2i раз. А для того чтобы полностью упорядочить
массив, потребуется не более
2 + 4 + … + 2(n – 1) = n (n – 1)
выполнений цикла.
Трудоемкость алгоритма в наихудшем имеет
порядок O(n2).
__________________________________________
Алгоритм обменной сортировки
for i:=1 to n-1 do
begin j:=i;
while (j>0)and(X[j]>X[j+1]) do
begin
z:=X[j]; X[j]:=X[j+1];
X[j+1]:=z;
j:=j-1;
end
end
Доказательство: метод абстракции
Инвариант внутреннего цикла:
{X[1] ≤ X[2] ≤ . . . ≤ X[j]},
{X[j+1] ≤ X[j+2] ≤ . . . ≤ X[i]},
«Набор значений в массиве X остается неизменным»
Инвариант внешнего цикла:
{X[1] ≤ X[2] ≤ . . . ≤ X[i]},
«Набор значений в массиве X остается неизменным»
__________________________________________
Общее количество выполнений внутреннего
цикла в наихудшем случае равно
1 + 2 + . . . + (n – 1) = n·(n – 1)/2.
Если массив уже упорядочен, то внутренний
цикл ни разу не будет исполняться, и тогда
трудоемкость алгоритма (в наилучшем!) имеет
порядок O(n).
__________________________________________
Алгоритм пузырьковой сортировки
for i:=n downto 2 do
for j:=1 to i-1 do
if X[j]>X[j+1] then
begin
z:=X[j]; X[j]:=X[j+1];
X[j+1]:=z
end
Общее количество выполнений внутреннего
цикла равно n·(n – 1)/2.
После очередного выполнения внутреннего
цикла наибольший среди первых i элементов
окажется на i-м месте в массиве.
________________________________________
Сортировка линейных списков
pk:=nil;
while pk<>p do
begin p1:=p; p2:=p1^.p;
while p2<>pk do
begin
if p1^.s>p2^.s then begin
z:=p1^.s; p1^.s:=p2^.s;
p2^.s:=z
end;
p1:=p2; p2:=p2^.p
end;
pk:=p1
end
Инвариант внутреннего цикла:
1) указатель p1 – на j-й элемент списка;
2) указатель p2 – на (j + 1)-й элемент списка;
3) j-й элемент списка имеет максимальное
содержимое (поле s) среди элементов списка с
1-го по j-й;
4) набор значений (полей s) всех элементов
списка остается неизменным.
Инвариант внешнего цикла:
1) указатель pk – на (i + 1)-й элемент списка;
2) элементы списка с (i + 1)-го по n-й –
упорядочены по возрастанию;
3) любой элемент списка с 1-го по i-й имеет
содержимое (поле s) не больше чем содержимое
элементов списка с (i + 1)-го по последний.
__________________________________________
Косвенная упорядоченность
Задается дополнительный массив In, называемый индексным:
A[In[1]]≤ A[In[2]]≤ ... ≤A[In[n]]
Модификация алгоритма сортировки:
1) вначале элементам массива In присвоить
начальные значения:
In[1]=1, In[2]=2, …, In[n]=n;
2) везде в программе, где элемент массива
A[j] используется в операции сравнения,
заменить A[j] на A[In[j]];
3) везде в программе, где элемент массива
A[j] используется в присваивании, заменить
A[j] на In[j].
_________________________________________
Задача слияния двух упорядоченных
массивов A и B в массив C
Алгоритм слияния. Входные массивы A и B
содержат соответственно n1 и n2 элементов,
результирующий массив C будет содержать
n1+n2 элементов.
i1:=1; i2:=1;
{1}
for j:=1 to n1+n2 do
{2}
if i1>n1 then
{3}
begin C[j]:=B[i2]; i2:=i2+1 end{4}
else if i2>n2 then
{5}
begin C[j]:=A[i1]; i1:=i1+1 end{6}
else if A[i1]<=B[i2] then
{7}
begin C[j]:=A[i1]; i1:=i1+1 end{8}
else
{9}
begin C[j]:=B[i2]; i2:=i2+1 end{10}
Инвариант цикла:
1) {C[1],...,C[j-1]}=
{A[1],...,A[i1-1],B[1],...,
B[i2-1]};
2) C[1]<=C[2]<=...<=C[j-1];
3) C[j-1]<=A[i1] при i1<=n1,
C[j-1]<=B[i2] при i2<=n2.
_________________________________________
2-й вариант алгоритма слияния
i1:=1; i2:=1; j:=1;
while (i1<=n1)and(i2<=n2) do
begin
if A[i1]<=B[i2] then
begin C[j]:=A[i1]; i1:=i1+1 end
else
begin C[j]:=B[i2]; i2:=i2+1 end;
j:=j+1
end;
while i1<=n1 do
begin
C[j]:=A[i1]; i1:=i1+1; j:=j+1
end;
while i2<=n2 do
begin
C[j]:=B[i2]; i2:=i2+1; j:=j+1
end;
Слияние двух упорядоченных по
возрастанию линейных списков
_______________________________________
procedure slist(var p1,p2,p3:pel);
var p4:pel;
begin
if p1^.s<=p2^.s then
begin p3:=p1; p4:=p1; p1:=p1^.p end
else
begin p3:=p2; p4:=p2; p2:=p2^.p end;
{наименьший элемент перемещен в выходной список}
while (p1<>nil)and(p2<>nil) do
{цикл, пока оба входных списка не пусты}
if p1^.s<=p2^.s then begin
p4^.p:=p1; p4:=p1; p1:=p1^.p
end
else begin
p4^.p:=p2; p4:=p2; p2:=p2^.p
end;
{в цикле наименьший элемент – в выходной список}
if p1<>nil then p4^.p:=p1
else p4^.p:=p2;
{оставшийся непустым входной список
присоединяется в конец выходного списка}
p1:=nil; p2:=nil
end;
Алгоритм слияния в виде процедуры
procedure S(var X,Y:mas;
b1,e1,e2:integer);
var i1, i2, j:integer;
begin i1:=b1; i2:=e1+1; j:=b1;
while (i1<=e1)and(i2<=e2) do begin
if X[i1]<=X[i2] then
begin Y[j]:=X[i1]; i1:=i1+1 end
else
begin Y[j]:=X[i2]; i2:=i2+1 end;
j:=j+1
end;
while i1<=e1 do begin
Y[j]:=X[i1]; i1:=i1+1; j:=j+1
end;
while i2<=e2 do begin
Y[j]:=X[i2]; i2:=i2+1; j:=j+1
end
end;
__________________________________________
Алгоритм сортировки слиянием
procedure sort(var X,Y:mas;
b,e:integer);
var c:integer;
begin
if b<e then begin
c:=(b+e)div 2;
sort(X,Y,b,c);
sort(X,Y,c+1,e);
S(X,Y,b,c,e);
for i:=b to e do
X[i]:=Y[i]
end
end;
_________________________________________
Доказательство
Базис. Размер N упорядочиваемого фрагмента в
массиве X равен 1. Алгоритм ничего не делает.
Предположение.
Пусть
алгоритм
умеет
упорядочивать фрагменты в массиве X размером от 1
до N = k ≥ 1 элементов.
Индукция. Пусть фрагмент в массиве X имеет
размер
N=k+1≥2
элементов. Тогда алгоритм
выполняет следующие действия:
1) делит фрагмент на две равные (или почти равные)
части;
2) каждую из частей (размером не более k элементов)
рекурсивно упорядочивает;
3) выполняет слияние двух упорядоченных частей в
один фрагмент массива Y;
4) копирует фрагмент массива Y в массив X на
место исходного фрагмента.
________________________________________________
Вызов процедуры sort для упорядочения
массива X из n элементов (при использовании
вспомогательного массива Y) должен быть таким:
sort(X,Y,1,n);
___________________________________
Если размер n упорядочиваемого массива
2m – 1 < n ≤ 2m,
то не более чем за m последовательных делений
размер фрагмента массива станет равным 1.
Т.е. глубина рекурсии также не превысит m,
т.е. log 2 n ,
что намного меньше размера
массива X.
_______________________________________________
Трудоемкость T (n):
 T (1)  1,

 T (n)  2T (n / 2)  cn,
где c – константа.
n  2, 3, ...,
Полагая 2m – 1 < n ≤ 2m, из (5.6) получим:
T (n) = 2 T (n / 2) + c n =
= 22 T (n / 4) + c n + c n = … ≤ c n m =
= c n log 2 n .
Рекуррентное соотношение для количества
рекурсивных вызовов Rn процедуры sort при
n = 2m:
 R1  1,

2
Rn  2 Rn / 2  1, n  2, 2 , ...
Отсюда получим:
Rn = 2Rn/2 + 1 = 4Rn/2 + 2Rn/2 + 1 = . . .
= 2∙2m – 1 ≈ 2 n.
В целом трудоемкость T (n) имеет порядок:
O(n log n).
Функции трудоемкости
n
4
10
20
100
1000
106
log 2 n n log 2 n
2
4
5
7
10
20
8
40
100
700
10000
20∙106
n2
n3
2n
16
64
16
100 1000 1024
400 8000 ≈106
10000 106
≈1030
106
109 ≈10300
1012 1018 ≈10300000
Процедура sortlist – рекурсивный алгоритм сортировки списков методом слияния.
p – указатель на начало входного списка.
n – количество элементов в списке.
Список перед выполнением процедуры
неупорядочен, после выполнения –полностью
упорядочен.
__________________________________________
procedure sortlist(var p:pel;n:integer);
var p1,p2:pel;
k,i:integer;
begin
if n>1 then begin
k:=n div 2; p1:=p;
for i:=1 to k-1 do p1:=p1^.p;
p2:=p1^.p; p1^.p:=nil; p1:=p;
{список разделен на две почти одинаковые части}
sortlist(p1,k);
sortlist(p2,n-k);
{обе части списка рекурсивно отсортированы}
slist(p1,p2,p)
{обе части списка объединены слиянием}
end
end;
Трудоемкость алгоритма тоже имеет порядок
O(n log n).
Download