- •Основы разработки алгоритмов
- •Издание посвящается 400-летию г. Томска
- •1.Рекуррентные алгоритмы
- •Задачи для самостоятельного решения 1
- •2.Подпоследовательности в массиве
- •Задачи для самостоятельного решения
- •3.Упорядоченность в массиве
- •Задачи для самостоятельного решения
- •4.Матрицы
- •Задачи для самостоятельного решения.
- •5.Обработка текста
- •Задачи для самостоятельного решения
- •6.Информационные массивы
- •Задачи для самостоятельного решения.
- •7.Советы по решению задач
- •8.Краткий справочник по языку паскаль Структура программы
- •Типы данных
- •Операторы
- •Литература
3.Упорядоченность в массиве
Поиск элемента с заданным значением. Дан упорядоченный целочисленный массив A, содержащий n элементов, и некоторое числовое значение p. Требуется найти такой номер i элемента массива, для которого A[i] = p, или определить, что такого номера нет.
Эта задача может быть решена просмотром элементов массива в цикле до первого совпадения элемента A[i] с p. Поскольку после нахождения искомого значения просматривать массив дальше нецелесообразно, в алгоритме следует использовать цикл while. Этот цикл завершится, когда будет достигнут конец массива или найдено искомое значение. Если при этом окажется, что параметр цикла стал больше, чем длина массива, то искомого значения в массиве нет. В противном случае элемент со значением p находится на месте i.
Алгоритм 1.
i:=1;
while (i<=n) and (A[i]<>p) do
i:=i+1;
if i>n
then writeln(‘элемент не найден’)
else writeln(‘элемент со значением ’,p,
‘стоит на месте ’,i);
Поиск можно значительно ускорить, если использовать свойство упорядоченности массива.
Идея быстрого алгоритма поиска состоит в том, что в массиве выделяется область "подозрительных" элементов, т.е. таких, что если искомый элемент в массиве существует, он обязательно будет внутри этой области. Каждое сравнение выполняется так, чтобы область сокращалась вдвое, поэтому алгоритм получил название дихотомического (деление пополам). Для этого на каждом шаге цикла производится сравнение p с элементом A[c], расположенным в середине "подозрительной" области. При этом возможны два случая:
1) A[c] < p, тогда из области "подозрительных" элементов отбрасываются все, начиная с начала области до номера c включительно;
2) A[c] >= p, тогда из области "подозрительных" элементов отбрасываются все, начиная с номера c + 1 до конца области.
Трудоемкость алгоритма. Для простоты предположим, что количество элементов n = 2 m. После одного сравнения длина массива уменьшается ровно в 2 раза, после 2-х сравнений – в 2 2 раза и т.д., после k сравнений – в 2 k раз. Таким образом, после m сравнений длина подозрительной области станет равной 1, и цикл прекратится. Следовательно, трудоемкость (число шагов цикла): m = log 2 n – логарифмическая.
Алгоритм 2.
b:=1; e:=n;
while b<e do
begin c:=(b+e)div 2;
if A[c]<p then b:=c+1 else e:=c
end;
if A[b]=p then i:=b else i:=0;
Слияние упорядоченных массивов. Даны два упорядоченных целочисленных массива: A, содержащий n1 элементов, и В из n2 элементов. Переписать все элементы из этих массивов в массив C так, чтобы массив C был упорядоченным.
Эту задачу можно было бы решить следующим образом: вначале скопировать все n1 + n2 элементов из массивов A и B в массив C, а затем упорядочить массив C. Однако такой алгоритм не учитывает упорядоченности исходных данных, т.е. условие задачи выполняется не полностью
Идея алгоритма слияния состоит в том, что на каждом шаге алгоритма сравнивается элемент из массива A с элементом из массива B (вначале первые элементы), и меньший из них переписывается в массив C. При этом следует учесть ситуацию, когда все элементы из одного массива уже переписаны, а из другого – нет.
Алгоритм 1.
Пошаговая разработка
начальные значения;
for i:=1 to n1+n2 do
переписать на i-е место в C;
начальные значения
i1:=1; i2:=1;
переписать на i-е место в C
if кончился A
then переписать из B
else if кончился B
then переписать из A
else переписать меньший из A или B
кончился A
i1>n1
переписать из B
C[I]:=B[i2]; i2:=i2+1;
кончился B и переписать из A детализируются по аналогии с предыдущими шагами.
переписать меньший из A или B
if A[i1]<=B[i2]
then переписать из A
else переписать из B;
Результат пошаговой разработки
i1:=1; i2:=1;
for i:=1 to n1+n2 do
if i1>n1 then
begin C[i]:=B[i2]; i2:=i2+1 end
else if i2>n2 then
begin C[i]:=A[i1]; i1:=i1+1 end
else if A[i1]<=B[i2] then
begin C[i]:=A[i1]; i1:=i1+1 end
else
begin C[i]:=B[i2]; i2:=i2+1 end;
Это решение можно записать по-другому с помощью циклов while. Первый цикл работает до тех пор, пока есть оба массива. Второй цикл копирует оставшийся хвост массива A (если он есть). Третий цикл копирует хвост массива B (если он есть).
Алгоритм 2.
Пошаговая разработка
начальные значения;
while есть оба массива do begin
переписать меньший элемент на i-е место в C;
i:=i+1
end;
переписать конец A;
переписать конец B;
начальные значения
i1:=1; i2:=1; i:=1;
есть оба массива
(i1<=n1)and(i2<=n2)
переписать меньший элемент на i-е место в C
if A[i1]<=B[i2] then
begin C[i]:=A[i1]; i1:=i1+1 end
else
begin C[i]:=B[i2]; i2:=i2+1 end;
переписать конец A
while i1<=n1 do begin
C[i]:=A[i1]; i1:=i1+1; i:=i+1
end
переписать конец B
while i2<=n2 do begin
C[i]:=B[i2]; i2:=i2+1; i:=i+1
end
Результат пошаговой разработки
i1:=1; i2:=1; i:=1;
while (i1<=n1) and (i2<=n2) do begin
if A[i1]<=B[i2] then
begin C[i]:=A[i1]; i1:=i1+1 end
else
begin C[i]:=B[i2]; i2:=i2+1 end;
i:=i+1
end;
while i1<=n1 do begin
C[i]:=A[i1]; i1:=i1+1; i:=i+1
end;
while i2<=n2 do begin
C[i]:=B[i2]; i2:=i2+1; i:=i+1
end;
Трудоемкость обоих алгоритмов– линейная.
Упорядочение массива. Дан целочисленный массив. Упорядочить его по возрастанию.
Пусть на вход алгоритма поступают:
1) количество чисел – переменная n;
2) целочисленный массив – переменная A. Номера элементов в массиве – от 1 до n.
Идея алгоритма (пузырек). В упорядоченном массиве для любой пары рядом стоящих элементов должно выполняться условие:
A[i] <= A[i+1].
Пусть алгоритм последовательно выполняет сравнения:
A[i] > A[i+1].
При каждом сравнении возможны два исхода:
1) A[i] > A[i+1], результат сравнения истинен, в этом случае элементы массива A[i] и A[i+1] должны обменяться своими значениями;
2) A[i] <= A[i+1], результат сравнения ложен, обмен делать не нужно.
Сравнения нужно выполнить последовательно от начала массива для всех пар соседних элементов. После однократного прохода по массиву самый большой элемент попадет на последнее место (пузырек всплывет). Он больше не будет участвовать в сравнениях, это следует учесть при записи цикла.
Для того, чтобы каждый элемент попал на свое место, нужно выполнить n-1 проходов по массиву.
Алгоритм 1.
for j:=1 to n-1 do
for i:=1 to n-j do
{(n - j) + 1 – длина неупорядоченной части массива}
if A[i]>A[i+1] then begin
z:=A[i]; A[i]:=A[i+1]; A[i+1]:=z
end;
Трудоемкость алгоритма. Во время исполнения внешнего цикла при j = 1 внутренний цикл работает (n – 1) раз, при j =2 – (n – 2) раза, и т.д. Таким образом, общее количество шагов внутреннего цикла – сумма элементов арифметической прогрессии n – 1, n – 2, …, 2, 1. Эта сумма равна n*(n – 1) / 2, т.е. трудоемкость – квадратичная.
Алгоритм 2.
Идею сравнения соседних элементов друг с другом можно использовать, выполняя проходы по массиву от i-го элемента к началу. Такая последовательность действий приводит к тому, что к моменту сравнения A[i] с A[i+1] первые i элементов оказываются упорядоченными. Поэтому для каждого нового значения, перемещающегося по направлению к началу массива, можно проводить сравнения только до тех пор, пока оно меньше своего соседа слева. Такой метод называется обменной сортировкой.
for j:=1 to n-1 do {цикл проходов по массиву}
begin i:=j;
while (i>0)and(A[i]>A[i+1]) do begin
z:=A[i]; A[i]:=A[i+1]; A[i+1]:=z;
i:=i-1
end {внутренний цикл закончился, при этом выполняется }
{ одно из условий: либо i=0, либо A[i]<=A[i+1]}
end;
Трудоемкость алгоритма. В худшем случае внутренний цикл выполнит такое же количество шагов, как в алгоритме 1, т.е. общая трудоемкость алгоритма 2 – квадратичная. Однако, если массив уже упорядочен, внутренний цикл вообще ни разу не будет выполняться, и трудоемкость алгоритма 2 будет линейной.
