ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 31.03.2021
Просмотров: 6811
Скачиваний: 51
271
1 Петров
2 Смирнов
3 Алексеев
3
4
1
Линейный связанный список - это конечный набор пар, состоящих
из информационной части (Info) и указующей части (Link).
N Кузнецов
2
Линейные связанные списки являются эффективной структурой данных для моделирования
ситуаций, в которых подвергаются изменениям упорядоченные массивы элементов данных. Осо-
бенно важно их использование при процедурах внесения или удаления элементов из середины
массива. Когда модификации касаются лишь начала или/и конца, то необходимость в связанных
списках отпадает, и становится достаточным использование одномерного исходного массива.
Здесь
на
помощь приходят стеки и очереди.
Пусть, например, задано арифметическое выражение. Требуется определить, правильно ли
расставлены в выражении скобки.
Для решения подобных задач используют стековую память (называемую просто «стек»).
Стек представляет последовательность данных и имеет лишь одну границу для добавления и уда-
ления элементов. В нашем случае в стек помещаются и удаляются скобки.
Первым необходимым условием правильности расстановок скобок является совпадение ко-
личества левых и правых скобок. Такой контроль легко осуществить введя счетчик top, который
при просмотре выражения и обнаружении левой скобки (допустим, что имеем только круглые
скобки '(' ) увеличивается на +1. Если на очередном месте встретилась правая скобка, то значение
счетчика уменьшается на 1. Тогда правильность расстановки определяется по итоговому значению
top.
Программа 36
program skobkal; (*проверка скобок по количеству*)
var top, i, n: integer; slovo: string[100]; skob: string[100];
begin
write('введи арифметическое выражение: ');
readln(slovo); n:length(slovo);
top:=0; skob:=''; i:=l;
while (i<=n) do begin
if slovo[i]=')' then begin top:=top+1; skob:=skob+slovo[i]
end;
if slovo[i]=')' then begin
top:=top-l; skob:=skob+slovo[i] end;
i:=i+l
end;
writeln(skob) ;
if top=0 then write('выражение правильное') i else
write('выражение неправильное');
readln .
e
nd.
Строковая переменная skob предназначена "для визуализации всех имеющихся скобок в
выражении.
В случае, когда в выражении используются фигурные, квадратные и круглые скобки, задача
усложняется тем, что необходим еще контроль соответствия левых и правых скобок. В этой связи
удобно использовать стек, в котором помещаются очередные левые скобки. При обнаружении
правой скобки из вершины стека извлекается левая скобка, помещенная последней, и проводится
их идентификация. Полный текст программы представлен ниже.
Программа 37
272
program skobka2; (*проверка расстановок скобок*) var
top, i, n: integer;
slovo: string[100];
store: array [1. . 100] of char; -x: char.; sicob: string[100];
p: boolean;
begin
write('введи арифметическое выражение: ');
readln(slovo); n:=length(slovo) ;
top:=0; p:=true; skob:=''; i:=1;
while (i<=n)and(p) do
begin if (slovo[i]='(') or (slovo[i]='[') or (slovo[i]='(')
then begin top:=top+l; store[top]:=slovo[i];
skob:=skob+slovo[i] end;
if slovo(i]='}' then begin x:=store(top];
if x<>'(' then p:=false
else begin top:=top-l; skob:=skob+slovo{i] end;
end;
if slovo[i]=']' then begin x:=store[top] ;
if x<>'[' then p:=false
else begin top:=top-l; skob:=skob+slovo[i] end;
end;
if slovo(i]=')' then begin x:=store(top] ;
if x<>'(' then p:=false
else begin top:=top-l; skob:=skob+slovo[i] end;
end;
i:=i+l end;
writeln(skob); if top=0 then write('выражение правильное')
else write('выражение неправильное');
readln
end.
Структура данных «очередь» используется для моделирования систем массового оослуж-
нвания: очереди людей в магазинах, транспортных потоков, производственных линий и т.п. Рас-
смотрим модельную ситуацию с формированием очереди в ком-нибудь учреждении сферы обслу-
живания, например, в банке.
Пусть задана скорость поступления клиентов в банк и известна скорость обслуживания.
Вместо скорости поступления клиентов будем задавать вероятность р их появления в единицу
времени. За скорость обслуживания примем число v, соответствующее времени обслуживания од-
ного клиента. Для простоты примем в качестве массива данных о клиентах банка числовой массив
со случайными числами из интервала 1..100. Для формирования очереди достаточно ввести две
переменные, которые указывают на начало и конец списка данных.
Следующая программа демонстрирует динамику обслуживания очереди.
Программа 38
program bank;
uses crt;
type item
=
integer;
var qq:array[l..100] of item; i, t, v, L, R; integer;
р, x: real; st: string[10];
begin (*начальное состояние очереди*)
qq[l]:=random(100); qq[2]:=random(100); qq[3]:=random(100);
L:=l; R:=3;
р:0,6
v:=2; randomize; t:=0;
repeat
t:=t+l; x:=random; if x<p then begin R:=R+1;
qq[R]:=random(100);
end;
273
if (t mod v=0) then L:=L+1;
until keypressed or (R>100) ;
(*вывод состояния очереди на момент прерывания*) for
i:=L to R do writeln(qq(i]);
readin;
end.
Контрольны
е
задания
1. Постройте программу упорядочивания списка фамилий студентов группы с использова-
нием линейного списка.
2. С помощью стека организуйте алгоритм, который определяет, является ли заданное сло-
во палиндромом («перевертышем»).
3. Придумайте задачу на использование очереди.
4.4. РЕКУРСИВНЫЕ АЛГОРИТМЫ
Изучая в предыдыщем разделе язык Паскаль, мы уже использовали понятие рекурсии. Од-
нако, оно столь важно и принципиально, что с ним следует познакомиться детальнее.
Рекурсией называют метод определения или вычисления функции, процедуры или решения
задачи посредством тон же функции, процедуры и т.д. Рекурсивные алгоритмы широко использу-
ют методы частных целей, подъема и отрабатывания назад. На эвристическом уровне рекурсия
позволяет эффективно использовать метод проб и ошибок.
Продолжим рассмотрение примера задачи тура шахматного коня из предыдущего раздела.
Приведенный там алгоритм строил возможный путь коня по простой стратегии очередного хода
на свободное место по принципу часовой стрелки. Однако, он не позволял гарантированно найти
полный тур коня. Применим простую эвристическую модель решения задачи - в случае отсутствия
возможности очередного хода осуществляется возврат коня на предыдущее поле и возобновление
поиска дальнейшего маршрута по другому пути. Подобный процесс называют возвратом (или от-
катом). Его можно осуществлять по универсальной схеме:
procedure
RETR;
begin
инициализация начального хода repeat выбор оче-
редного хода
if подходит then его запись;
if решение не полное then RETR;
if неудача then стирание хода и возврат
на предыдущий until удача or нет хода
end.
Подобная рекурсивная процедура и уже известный алгоритм, рассмотренный выше, позво-
ляют построить нужную программу. Ниже представлена программа тура коня для произвольного
поля NxN, позоляющая отыскивать полный тур с любого начального положения. Для наглядной
иллюстрации процесса поиска в глубину и в ширину с возвратами в программе в комментарные
скобки обозначены команды вывода промежуточных результатов.
Программа
39
program tur;
var i, j, ii, jj, n, nn: integer; q: boolean;
dx, dy:array[1..8] of integer; h:array[1..8,1..8] of integer;
(*рекурсивная процедура - попытка сделать ход*)
procedure try(i,x,у:integer; var q:boolean);
var k, u, v: integer; ql: boolean;
begin
k:=0; repeat k:=k+l; ql:=false; u:=x+dx[k]; v:=y+dy(k];
if ( (1<=u) and(u<=n) and (1<=v) and (v<=n) ) and(h[u,v]=0)
then begin h[u,v]:=i;
274
(*для отладки и наблюдения процесса поиска с возвратом*')
for ii:=l to n do begin for jj:= 1 to n do
write(h[ii,jj]:5); writeln;
end;
readin;
if i<nn then begin try(i+l,u,v,ql);
if not(ql) then h[u,v]:=0
else ql:=truer;
end
else ql:=true
end;
until (ql) or (k=8);
q:=ql
end; (* конец процедуры*)
begin
dx[l] =2: dx[2]:=l; dx[3]:=-l; dx[4]:=-2; dx[5]:=-2;
dx[6] =-1: dx[7]:=l; dx[8]:=2; dy[l]:=l; dy[2]:=2;
dy[3] =2: dy[4]:=l; dy[5]:=-l; dy[6]:=-2;
dy[7] =-2: dy[8]:=-1;
write ('введи n: '); readln(n);
for i =1 to n do for j:=1 to n do h[i,j]
:=0;
write; ('введи i,j : '); readln(i,j); nn:=n*n;
h[i,j]:=l; try(2,i,j,q);
if q then begin
for i:=l to n do begin
for j:= 1 to n do write(h[i,j]:5);
writeln;
end;
end ' else writeln( 'нет маршрута');
readln
end.
Для n = 5 и n = 6 алгоритм быстро находит искомые туры коня. Для n = 8 время решения
может возрасти в несколько десятков раз.
Рассмотрим еще два замечательных рекурсивных алгоритма, позволяющих строить регу-
лярные образы, в конечном счете образующие красивые узоры на экране дисплея. Узор образуется
из серии выстраиваемого определенным образом заданного мотива. '
Ниже представлена программа, использующая при построении узора кривые Серпинского,
рис. 3.13.
Рис. 3.13.
Примеры кривых Серпинского
На рисунке изображены кривые Серпинского S1 и S2 первого и второго порядков. Кривую
Серпинского Si можно разбить на 4 части: Ai, Bi, Ci, Di, которые соединяются четырьмя отрезка-
ми Эти четыре части кривой представляют одну и ту же ломаную, поворачивающуюся каждый раз
на 90 градусов. Нетрудно увидеть рекурсивные схемы, по которым ломаные Ai, Bi. Ci, Di получа-
ются из кривых A(i-l). B(i-l), C(i-l), D(i-l), размеры которых при этом сокращаются вдвое:
275
Ai: A(i-l)
Bi: B(i-l)
Ci: C(i-l)
Di: D(i-l)
B(i-l) –
C(i-l) |
D(i-l) –
A(i-l) |
D(i-l)
A(i-l)
B(i-l)
C(i-l)
A(i-l)
B(i-l)
C(i-l)
D(i-l)
Векторы, соединяющие отдельные элементы кривых, образуют с осью абсцисс углы, крат-
ные pi/4; средние векторы во всех схемах имеют длину, в два раза большую, чем крайние. Для по-
строения вектора длины U под углом T*pi/4 к оси абсцисс в программе описана процедура
linep(T,U:integer):
Программа 40
program serpinsk;
uses crt,graph;
const sq=512;
var i, xO, yO, x, y, t, u, gd, gm: integer; ch:chart;
procedure linep(t,u:integer);
var xl, yl:integer;
begin x:=x+round(u*cos(t*pi/4)) ;
y:=y-round(u*sin(t*pi/4)); lineto(x,y) ;
end;
procedure b(i:integer); forward;
procedure с(i:integer); forward;
procedure d(i:integer); forward;
procedure a(i:integer);
begin
if i>0 then begin a(i-l); linep(7,u); b(i-l); linep(0,2*u);
d(i-l); linep(l,u); a(i-l)
end end; •
procedure b;
begin
if i>0 then begin b(i-l); linep(5,u); c(i-l); linep(6,2*u);
a(i-l); linep(7,u); b(i-l)
end end;
procedure c;
begin
if i>0 then begin c(i-l); linep(3,u); d(i-l); linep(4,2*u) ;
b(i-l); linep(5,u);C(i-l)
end end;
procedure d;
begin
if i>0 then begin d(i-l); linep(l,u); a(i-l); linep(2,2*u);
c(i-l); linep(3,u); d(i-l) end
.
end;
begin gd:=0; initgraph(gd, gm, ' ' );
u:=sq div 4; x0:=320; y0:=128; i:=0;
repeat
i:=i+l; x0:=x0-u; u:=u div 2; y0:=y0-u;
x:=x0; y:=y0; setcolor(2*i);
moveto(x,y); a(i); linep(7,u); b(i);