Professional Documents
Culture Documents
методичка
методичка
МНОЖИННІ ТИПИ
В АЛГОРИТМІЧНІЙ МОВІ
ПРОГРАМУВАННЯ
1. МЕТА РОБОТИ
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
2
описі змінних, тип їхніх значень може бути заданим або безпосередньо, або
вказанням імені типу, яке визначене у розділі типів.
Наприклад:
TYPE num = (one, two, three, four);
number = set of num;
• VAR QU: number;
QP: set of num;
Для присвоювання значень змінним-множинам використовується опера-
тор присвоювання виду:
V: = S;
де V – змінна множинного типу;
S – множинний вираз, тобто вираз, значенням якого є множина.
Для наведеного вище прикладу допустимими є конструкції:
QU: = [one, two];
QP: = [three, four];
Задання значень множинам можливе за допомогою константних виразів
у розділі опису констант:
Const
Num = [’0’.. ’9’];
A = [’A’..’Z’,’a’..’z’];
P = A + Num; {константа Р містить цифри та
латинські літери}
Якщо дано опис змінної множинного типу:
VAR
PM : SET OF (LVIV, KYIV, ODESSA);
то усіма можливими значення цієї змінної у ПАСКАЛІ будуть:
[LVIV, KYIV, ODESSA]
[LVIV, KYIV]
[KYIV, ODESSA]
[LVIV, ODESSA]
[LVIV]
[KYIV]
[ODESSA]
[]
Елементи множини є не впорядкованими, тому однаковими є множини:
[1, 7, 4], [4, 7, 1], [1, 4, 7], [ 4, 1, 7], [7, 4, 1], [7, 1, 4] тощо.
3
2.2. ОПЕРАЦІЇ НАД МНОЖИНАМИ. МНОЖИННІ ВИРАЗИ
4
Наприклад:
Вираз ([1,2,3,4,5] + [2,5,6,7,8]) – ([1,2,3,4,5] *
[2,5,6,7,8]) дає в результаті множину [1,3,4,6,7,8].
+ Змінна
а)а) -
в)
[ Множинний вираз
*
Множник [
б)б) *
Індексний вираз
вираз> <Множник>
. . Індексни
г)
Доданок Конструктор множини
,
+ Змінна
в) в)
- [ Множинний вираз ]
*
<Конструктор множини>
Множник [ ]
Індексний вираз
*
. . Індексний вираз
г)
5
одного і того самого множинного типу, в операції IN перший операнд повинен
належати базовому типу, а другий – множинному типу значень, побудованому
на основі цього базового типу.
Результатом операцій відношення є логічне значення (TRUE або FALSE).
Таблиця 1
Операції відношення над множинами
Математичний Запис мовою Значення результату
запис Паскаль True False
Якщо множини А та В
збігаються
B
У протилежному
A=B A=B
випадку
A
Якщо множини А та В
не збігаються
У протилежному
AB A<>B B
випадку
Якщо елемент х
входить у множину А
x
У протилежному
xA x IN A
випадку
6
2.3. ВВЕДЕННЯ ТА ВИВЕДЕННЯ МНОЖИН
7
Таблиця 2
Процедури для роботи з множинами у Turbo Pascal 7
Структура виклику процедури Призначення Еквівалентна дія
Іnclude(<множина>, Включення у зазначену S=S+[I]
<елемент>) множину S нового
елемента I
Exclude(<множина>, Вилучення зазначеного S:=S-[I]
<елемент>) елемента I з множини S
8
1 Множина L, яка використовується у
Початок
програмі, за необхідності може бути отри-
2
мана з двох інших множин:
D=['0'..'9']
L=['a'..'z,'A'..'Z'']
SL=[’a’..’z’];
3 BL=[’A’..’Z’];
'testset.txt' L:=SL+BL;
Можлива також зворотна дія: з мно-
4
ні
жини L отримати SL та BL за допомогою
Не кінець
файлу а
операції різниці множин.
так SL:=L-[’A’..’Z’];
5
Ввід з BL:=L-[’a’..’z’];
файлу а
ch
Приклад: у заданій послідовності
6
так
7 літер, яка складається із букв латинського
ch D kd:=kd+1 алфавіту і закінчується крапкою,
ні
визначити загальну кількість входжень у
неї букв “а”, “е”, “о”, “u”.
8 9
так
ch L kl:=kl+1
Блок-схему алгоритму розв’язання
наведено на рис. 3.
ні
Текст програми мовою Паскаль:
10
файл BEGIN
'testset.txt'
K:=0;
READ(SYMB);
12
Кінець
WHILE SYMB<>’.’ DO
BEGIN
Рис. 2. Блок-схема алгоритму
Рис. 2 Блок-схема алгоритму розв'язку IF SYMB IN
розв’язання [’A’,’E’,’O’,’U’]
THEN K := K+1;
READ(SYMB);
END;
WRITELN (’ЗАГАЛЬНЕ ЧИСЛО ВХОДЖЕНЬ РІВНЕ ’, K)
END.
9
1
Початок
k=0
Ввід
SYMB
4
ні
SYMB '.'
так
5 6
так
SYMB
k:=k+1
[A,E,O,U]
ні
Ввід
SYMB
Вивід
Кінець
10
FOR I:=1 TO 100 DO
IF I IN X THEN WRITE(I,’ ’)
END.
У результаті виконання програми значенням множини Х буде [4, 5, 7,
12, 13, 14].
Приклад: задано множину натуральних чисел від 1 до n (n не може
перевершувати 255). З цієї множини необхідно одержати нову множину, що
містить тільки ті з натуральних чисел від 1 до n, що є квадратами
натуральних чисел.
Текст програми мовою Паскаль:
PROGRAM SETS (INPUT, OUTPUT);
VAR
N, I: INTEGER;
S: SET OF 1..255;
BEGIN
WRITE(’ЗАДАЙТЕ КІЛЬКІСТЬ ЕЛЕМЕНТІВ МНОЖИНИ’);
WRITELN(’N <= 255): ’);
READLN(N);
S := [1..N];
{Задаємо множину, що містить натуральні числа з n
чисел}
FOR I := 1 TO N DO
BEGIN
{Для кожного елемента I початкової
множини S перевіряємо, чи є він квадратом
другого натурального числа. Якщо елемент
I не є квадратом другого числа, він
вилучається з початкової множини}
IF TRUNC(SQRT(I)) <> SQRT(I) THEN
S:=S-[I];
{Якщо елемент I є квадратом другого числа,
він лишається у множині S. Виводимо його
на екран}
IF I IN S THEN WRITE(I:5)
END;
{Після виконання циклу множина s
містить лише ті натуральні числа, які
є квадратами інших чисел}
WRITELN
END.
11
Приклад: задано три множини символьного типу, які задані конструкт-
торами:
Y1=[’A’, ’B’, ’D’, ’R’, ’M’];
Y2=[’R’, ’A’, ’H’, ’D’];
Y3=[’A’, ’R’];
Сформувати нову множину: X=(Y1 Y2) (Y1\Y2). Вивести на друк
отриману множину. Перевірити, чи включена множина Y3 у множину Х.
Для формування нової множини Х доцільно скористатися оператором
присвоювання. Для виведення на екран елементів нової множини застосо-
вується оператор циклу FOR. Параметром циклу є символьна змінна С, що
набуває значення кожного символу латинського алфавіту від “А” до “R”. В
операторі циклу використовується не весь латинський алфавіт від “А” до “Z”, а
лише його частина. Це пов’язано з тим, що задані множини Y1, Y2, Y3 не
містять символів після літери “R”. Але помилкою не буде, якщо кінцевим
значенням параметра циклу буде символ “Z”.
Текст програми мовою Паскаль:
PROGRAM CREATESET (INPUT,OUTPUT);
VAR
Y1,Y2,Y3,X:SET OF CHAR;
C:CHAR;
BEGIN
Y1:=[’A’, ’B’, ’D’, ’R’, ’M’];
Y2:=[’R’, ’A’, ’H’, ’D’];
Y3:=[’A’, ’R’];
X:=(Y1*Y2)+(Y1-Y2);
WRITELN(’МНОЖИНА X=’);
FOR C:= ’A’ TO ’R’ DO
IF C IN X THEN WRITE(C:2);
WRITELN;
IF Y3<=X THEN WRITE(’Y3 МІСТИТЬСЯ В X’)
ELSE WRITE(’Y3 НЕ МІСТИТЬСЯ В X’);
END.
Приклад: з множини цілих чисел 1...20 виділити окремо: 1) множину чисел,
що діляться на 6 без остачі; 2) множину чисел, що діляться без остачі на 2 і 3.
Нехай К – розмірність множини; N2 – множина чисел, які діляться на 2
без остачі; N3 – множина чисел, які діляться на 3 без остачі; N6 – множина
чисел, які діляться на 6 без остачі; N23 – множина чисел, які діляться на 2 і на 3
без остачі; І – параметр циклу. Блок-схему алгоритму наведено на рис. 4.
12
1
Початок
2 А
k=20 В
N2=Ж 9
15
N3=Ж
i=1, k
i=1, k
3
i=1, k
11
10 ні 16 17
Вивід ні
i N2 Вивід
4
i i N6
5 i
так
остача
N2:=N2+[i] так
i / 2 =0 так
ні
18
12
6 так 7 i=1, k
остача i=1, k
N3:=N3+[i]
i / 3 =0
19 20
ні 14 ні
13 ні Вивід
Вивід i N23
i N3 i
8 i
20
А В Кінець
13
WRITELN(’МНОЖИНА ЧИСЕЛ, ЩО ДІЛЯТЬСЯ НА 3’);
FOR I:=1 TO K DO
IF I IN N3 THEN WRITE(I:3);
WRITELN;
WRITELN(’МНОЖИНА ЧИСЕЛ, ЩО ДІЛЯТЬСЯ НА 6’);
FOR I:=1 TO K DO
IF I IN N6 THEN WRITE(I:3);
WRITELN;
WRITELN(’МНОЖИНА ЧИСЕЛ, ЩО ДІЛЯТЬСЯ НА 2 ТА НА 3’);
FOR I:=1 TO K DO
IF I IN N23 THEN WRITE(I:3);
END.
14
Лабораторна робота № 11
КОМБІНОВАНІ ТИПИ (ЗАПИСИ,
СТРУКТУРИ)
В АЛГОРИТМІЧНІЙ МОВІ
ПРОГРАМУВАННЯ.
1. МЕТА РОБОТИ
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
2
службовим словом RECORD та закінчується словом END.
Типи полів T1,T2,…,Tn можуть набувати:
1) будь-який стандартний тип: REAL, INTEGER, CHAR, BOOLEAN;
2) ім’я типу, описаного раніше;
3) сам тип (перелічуваний, інтервальний тощо)
Синтаксичну діаграму комбінованого типу наведено на рис. 1.
,
;
3
символів ’СЕРГІЙ ’. Де – символ пропуска (пробіл).
Третій оператор занесе у поле RIK змінної STUDENT ціле число 1981.
Кожне поле запису можна не тільки задавати в операторі присвоювання,
але й вводити з клавіатури або з файла:
FOR I := 1 TO 30 DO
BEGIN
FOR J := 1 TO 10 DO
READ (GRUP [I].PR [J] );
FOR J := 1 TO 10 DO
READ (GRUP [I].IM [J] );
READLN (GRUP[I].DAT, GRUP[I].MIS, GRUP[I].RIK)
END;
За допомогою цього фрагмента програми з клавіатури буде введено
анкетні дані 30-ти студентів у такій формі:
ТКАЧУК СЕРГІЙ 30 01 1981
Тобто в одному рядку буде набрано інформацію про одного студента.
Слід пам’ятати:
1. Ім’я поля завжди вказується явно, а не обчислюється як індекс у маси-
вах.
2. Поля як самостійні програмні об’єкти поза записом не існують, тому
вказувати тільки ім’я поля без імені змінної не можна.
Оскільки поля PR та IM описані як масиви символів, то доцільно вико-
ристовувати цикл FOR під час введення, виведення та роботи з цими полями
(цикл FOR J := 1 TO 10 DO у наведеному прикладі).
Для повних змінних однакового комбінованого типу може викорис-
товуватися єдина операція – операція присвоювання. Наприклад:
GRUP [3] := STUDENT;
За допомогою цього оператора всі поля третього елемента масиву
GRUP заповнюються інформацією, що міститься у змінній STUDENT, тобто:
GRUP [3]. PR буде ‘ТКАЧУК ’
GRUP [3]. IM буде ‘СЕРГІЙ ’
GRUP [3]. RIK буде 1981 тощо.
Над полями можна виконувати ті операції, які допустимі для цього
типу. Так, якщо тип окремого поля INTEGER або REAL, то й операції над
цими полями будуть: +, -, *, /, DIV, MOD та операції порівняння <, >, <=, >=, =,
< >.
4
Приклад: обчислити суму, різницю та добуток двох комплексних
чисел X та Y.
Текст програми мовою Паскаль:
PROGRAM COMP (INPUT, OUTPUT);
TYPE
COMPL = RECORD
RE: REAL;
IM: REAL
END;
VAR X, Y, U, V, W : COMPL;
BEGIN
WRITE (’INPUT X.RE, X.IM’);
READLN(X.RE, X.IM);
WRITE(’INPUT Y.RE, Y.IM’);
READLN(Y.RE, Y.IM);
{U=X+Y}
U.RE := X.RE + Y.RE;
U.IM := X.IM + Y.IM;
WRITELN (’X+Y=’, U.RE, ’+’, U.IM, ’*I’);
{V=X-Y}
V.RE := X.RE - Y.RE;
V.IM := X.IM - Y.IM;
WRITELN (’X-Y=’, V.RE, ’+’, V.IM, ’*I’);
{W=X*Y}
W.RE := X.RE * Y.RE;
W.IM := X.IM * Y.IM;
WRITELN (’X*Y=’, W.RE, ’+’, W.IM, ’*I’)
END.
5
DAT : 1..31;
MIS : 1..12;
1
Початок
2
і = 1, 30
3
Ввід групи
4
L= false
5
і = 1, 30
6 ні 10
Міс =8 ні
Міс =7
так так
7 23≤чис ні
11 ні
1чис22
≤31
так так
12 Вивід
8 Вивід
прізвища
прізвища
9 13
L= true
L= true
14
ні
L =false
так
15
Вивід
Левів нема Рис. 2. Блок-схема алгоритму для задачі про
16 знак Зодіаку
Кінець
6
RIK : 1970..1990;
END;
VAR GRUP : ARRAY [1..30] OF STUD;
L : BOOLEAN;
I,J : INTEGER;
BEGIN
WRITELN (’ВВЕДIТЬ СПИСОК СТУДЕНТIВ’);
FOR I :=1 TO 30 DO
BEGIN
FOR J := 1 TO 10 DO
READ (GRUP[I].PR[J]);
FOR J := 1 TO 10 DO
READ (GRUP[I].IM[J]);
READLN (GRUP [I].DAT, GRUP [I].MIS, GRUP[I].RIK)
END;
L:=FALSE;
{ПЕРЕВIРКА ДАТИ НАРОДЖЕННЯ I ВИВIД}
FOR I := 1 TO 30 DO
BEGIN
IF((GRUP[I].MIS=7)AND(GRUP[I].DAT>=23)AND(GRUP[I].DAT<=31))
OR((GRUP[I].MIS=8)AND(GRUP[I].DAT>=1)AND(GRUP[I].DAT<=22))
THEN
BEGIN
FOR J := 1 TO 10 DO
WRITE (GRUP[I].PR[J]);
WRITELN;
L:=TRUE
END
END;
IF L = FALSE THEN WRITELN (’ЛЕВIВ У ГРУПI НЕМАЄ’)
END.
7
досить довгі програми, оскільки весь час необхідно записувати ім’я змінної GRUP[i].
Щоб спростити такі записи, у мові Паскаль введено оператор приєднання WITH.
Структура оператора WITH:
WITH <ім’я змінної> DO <оператор>;
Синтаксичну діаграму оператора WITH наведено на рис. 3.
8
BEGIN
FOR J :=1 TO 10 DO
WRITE(PR[J]);
WRITELN;
L := TRUE
END
END;
IF L = FALSE THEN
WRITELN (’ЛЕВІВ У ГРУПІ НЕМАЄ’);
END.
Текст програми суттєво скоротився.
Таким чином оператор приєднання WITH дає змогу використовувати
замість складеного імені безпосередньо ім’я поля. При цьому ім’я запису
виноситься у заголовок оператора приєднання. На етапі трансляції ім’я змінної
підставляється автоматично до імені поля і формується повне ім’я.
9
Оператор звертання до поля рік буде мати такий вигляд:
STUDENT.DATAN.RIK :=1981;
Приклад: обчислити кількість студентів Вашої групи, які народилися у
1981 році.
Текст програми мовою Паскаль:
PROGRAM TEST (INPUT, OUTPUT);
TYPE
STUD = RECORD
PR, IM : ARRAY [1..10] OF CHAR;
DATAN : RECORD
DAT : 1..31;
MIS : 1..12;
RIK : 1970..1990;
END
END;
VAR GRUP : ARRAY [1..30] OF STUD;
I, J, K : INTEGER;
BEGIN
FOR I :=1 TO 30 DO
WITH GRUP[I] DO
BEGIN
FOR J :=1 TO 10 DO READ (PR[J]);
FOR J :=1 TO 10 DO READ (IM[J]);
READLN (DATAN.DAT, DATAN.MIS, DATAN.RIK);
END;
FOR I :=1 TO 30 DO
WITH GRUP[I].DATAN DO
IF (RIK = 1981) THEN K :=K+1;
WRITELN (’КІЛЬКІСТЬ СТУДЕНТІВ, ЯКІ НАРОДИЛИСЯ В 1981
РОЦІ = ’, K);
END.
10
робітникам необхідно вказати:
1) прізвище;
2) ім’я;
3) по батькові;
4) рік народження.
Це спільна частина запису.
У варіантній частині слід вказати для жінок:
1) чи заміжня;
2) кількість дітей;
для чоловіків:
1) військовозобов’язаний;
2) військова спеціальність.
Опис такого типу буде мати вигляд:
TYPE
STAT = (MEN, WOM);
ANK = RECORD
PR, IM, PB : ARRAY [1..10] OF CHAR;
RIK : 1990..2000;
MW : STAT;
CASE STAT OF
MEN : (VZ : 0..1; SPEC: ARRAY [1..10] OF CHAR);
WOM : (MER : 0..1; KDIT : INTEGER)
END;
11
му вигляді вказати тип у заголовку CASE не можна.
7. Не можна використовувати одне й те саме ім’я для визначення
спільної та варіантної частини запису.
8. Звернення до полів записів із варіантом аналогічне зверненню до
звичайних записів:
Якщо описати змінні:
VAR
STUDENT : ANK;
GRUP : ARRAY [1..30] OF ANK;
То оператори присвоювання значення полям запису будуть:
STUDENT.PR := ’ТКАЧУК’;
STUDENT.IM := ’СЕРГІЙ’;
STUDENT.MW := MEN;
STUDENT.VZ := 1;
STUDENT.SPEC := ’ПРОГРАМІСТ’;
Приклад: за анкетними даними визначити і вивести на друк прізвища
студентів, які мають військову спеціальність “Програміст”.
Текст програми мовою Паскаль:
PROGRAM TEST (INPUT, OUTPUT);
TYPE
STAT = (MEN, WOM);
ANK = RECORD
PR, IM, PB : ARRAY [1..10] OF CHAR;
RIK : 1900..2001;
MW : STAT;
CASE STAT OF
MEN:(VZ : 0..1; SPEC : STRING [10]);
WOM:(MER : 0..1; KDIT : INTEGER);
END;
VAR
GRUP : ARRAY [1..30] OF ANK;
L : BOOLEAN;
I, J : INTEGER;
MENWOM : 1..2;
BEGIN
{ВВІД АНКЕТНИХ ДАНИХ}
FOR I :=1 TO 30 DO
WITH GRUP[I] DO
BEGIN
12
WRITELN (’ВВЕДИ ПРІЗВИЩЕ,ІМ’’Я, ПО БАТЬКОВІ, РІК
НАРОДЖЕННЯ ’);
FOR J :=1 TO 10 DO READ(PR[J]);
FOR J :=1 TO 10 DO READ(IM[J]);
FOR J :=1 TO 10 DO READ(PB[J]);
READLN(RIK);
WRITE(’ВВЕДІТЬ СТАТЬ ЧОЛ.- 1, ЖІН. - 2’);
READ(MENWOM);
CASE MENWOM OF
1 : MW := MEN;
2 : MW := WOM
END;
CASE MW OF
MEN : BEGIN
WRITE(’ВІЙСЬКОВОЗОБОВ’’ЯЗАНИЙ ТАК –1, НІ -0’);
READLN(VZ);
WRITE(’ВІЙСК.СПЕЦІАЛЬНІСТЬ’);
READLN(SPEC);
END;
WOM : READLN (MER, KDIT);
END
END;
L := FALSE;
FOR I :=1 TO 30 DO
WITH GRUP[I] DO
BEGIN
IF (SPEC = ’PROGRAMIST’) THEN
BEGIN
FOR J :=1 TO 10 DO WRITE(PR[J]);
WRITELN;
L := TRUE
END;
END;
IF L = FALSE THEN
WRITELN(’ПРОГРАМІСТІВ НЕМАЄ’)
END.
13
Лабораторна робота № 12
ДИНАМІЧНІ ОБ’ЄКТИ
АЛГОРИТМІЧНОЇ МОВИ
ПРОГРАМУВАННЯ.
ВКАЗІВНИКОВІ ТИПИ
1. МЕТА РОБОТИ
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
2
Динамічними називаються такі програмні об’єкти, які виникають
під час виконання програми, або розмір яких визначається чи змінюється
під час виконання програми.
Для роботи з динамічними об’єктами у мові Паскаль передбачено спе-
ціальний тип – тип вказівника.
Значення цього типу є вказівник на конкретний програмний об’єкт, за
яким здійснюється доступ до цього об’єкта. Іншими словами вказується адреса
комірки пам’яті, де зберігається відповідний об’єкт.
У мові Паскаль для опису дій над динамічними об’єктами кожному
такому об’єкту зіставляється статична змінна вказівникового типу.
Синтаксис задання вказівникового типу:
<заданий вказівниковий тип> = <ім’я типу>
де – це ознака вказівникового типу,
<ім’я типу> – ім’я або стандартного, або раніше описаного, створеного
Вами типу.
Значенням змінних типу вказівника є вказівники на динамічні об’єкти
того типу, ім’я якого вказано після стрілки.
Змінні вказівникового типу описуються у розділі опису змінних та їхній
тип визначається або у розділі опису типів, або безпосередньо заданням типу
при описі змінної, але лише іменем.
Наприклад, фрагмент програми:
PROGRAM DINOB (INPUT, OUTPUT);
TYPE
MAS = ARRAY [1..50] OF INTEGER;
DINMAS = MAS;
VAR
P : INTEGER;
Q : CHAR;
AM, BM : DINMAS;
. . .
Значення змінної Р – вказівник на динамічний об’єкт цілого типу; Q –
вказівник на динамічний об’єкт символьного типу; АМ, ВМ – вказівники на
динамічні об’єкти, значення яких є масив цілих чисел.
Зв’язок вказівника з об’єктом схематично можна зобразити так:
P Об'єкт
*
3
* – значення вказівника Р;
→ (стрілка) – відображує вказівник на об’єкт, через який цей об’єкт
доступний у програмі.
Якщо вказівник не зв’язаний з жодним об’єктом, таке значення задається
службовим словом NIL
Схематично оператор присвоєння P := NIL; відображається:
P
*
4
найпростішому випадку – це ім’я змінної вказівникового типу після якого
стоїть стрілка, тобто:
Р – це значення того програмного об’єкта, на який вказує вказівникова
змінна Р.
Після породження динамічних змінних P та Q можна присвоїти їм
початкові значення:
P = Q := 'A';
P
* 25Q Q * 'A'
схематичне зображення
Змінна з вказівником може використовуватися у будь-яких конструкціях
мови. Але тут треба звертати увагу на тип вказівникової змінної.
Не можна записати:
P := P+Q;
5
• Основне правило: над значеннями вказівникового типу не можна
виконувати ніяких операцій, які давали б результат цього самого типу.
Значення вказівникового типу – це адреса тих чи інших програмних об’єктів і
змінювати її на свій розсуд не можна, оскільки розміщенням усіх об’єктів у
пам’яті машини займається транслятор.
• До значень вказівникового типу можуть застосовуватись деякі опе-
рації порівняння, а також вони можуть бути операндами в операторі
присвоювання.
• У загальному оператор присвоювання має вигляд:
• V := E;
P := Q;
VAR P, D : INTEGER;
6
Після виконання оператора D := NIL (рис. 1, г) розривається зв’язок D з
об’єктом 25 і на нього буде вказувати лише змінна Р.
Слід чітко розрізняти вказівник на змінну і змінну з вказівником, тобто
сам об’єкт (рис. 2).
NEW(P); NEW(D)
P D
* Q *
а)
P := 5; D := 25
P 5 D 25
* Q *
б)
P := D;
P 5 D 25
* Q *
в)
D := NIL;
P 5 D 25
* Q *
г)
P 5
P := 5;
*
D := 25 D 25
*
P P 25
*
5
P := D;
*
P := D;
D 25
D
* 25
*
7
Рис. 2. Зміна значень вказівникової змінної P := D
і змінної із вказівником P := D
PROGRAM DIN2;
VAR
P *
P, D :INTEGER; a)
BEGIN
D *
NEW(P); P * Q
NEW(D);
D * б)
P := 5;
P * 5Q
D := 25; D * 25 в)
DISPOSE(P);
P * 5
D * 25 г)
P
P := D;
*
D * 25 д)
P
D := NIL; *
END. 8 D * 25 е)
Рис. 3. Приклад механізму роботи з динамічними об’єктами операторами програми
Використовувати процедуру DISPOSE треба дуже обережно, щоб не
знищити потрібні об’єкти (рис. 4).
Приклад:
PROGRAM DIN4;
VAR
P
*
P, D : INTEGER; а)
BEGIN
D *
NEW(P);
NEW(D);
P
*
б)
D *
P := 5;
P
* 5
D := 25; D * 25 в)
P 5
P := D; * г)
D * 25
P 5
DISPOSE(P); *
END.
D * 25 д)
9
Під рядком (або словом) розуміють упорядковану послідовність символів
конкретного алфавіту.
Рядок може бути поданий у вигляді масиву – тобто статичного об’єкта.
Наприклад, слово “АЛГОРИТМ” можна подати у вигляді вектора
ARRAY [1..10] OF CHAR.
Під час роботи з рядком найчастіше здійснюються такі три операції:
1) пошук входження заданого символу в рядок;
2) вставка заданого символу у вказане місце рядка;
3) видалення заданого символу із вказаного місця рядка.
Оскільки доступ до кожного елемента рядка, описаного як вектор,
здійснюється через індекс цього елемента, то для процедури вставки необхідно
спочатку розсунути рядок і лише після цього вставити потрібний елемент.
Тобто усі елементи масиву, які розміщені після вставленої літери, необхідно
пронумерувати індексом на одиницю більшим від попереднього.
Аналогічна ситуація з видаленням визначеного елемента. Тому недоліка-
ми векторного подання рядка є:
1. На операцію вставки та видалення витрачається занадто багато часу че-
рез необхідність зсувати частину слова і пошуку маркера кінця рядка.
2. Якщо довжина рядка наперед невідома, то необхідно резервувати пам’ять
із запасом.
Але кожен рядок можна зобразити у вигляді ланцюжка символів, у якому
кожен попередній елемент містить вказівник на наступний (рис. 5).
* * * * * * * *
Рис. 5. Динамічний рядок символів слова “АЛГОРИТМ”
10
Кожна ланка запису складається з двох полів: поле ELEM містить
безпосередньо символьні елементи рядка, поле NEXT містить вказівник на
наступний елемент.
При описі типу ZV ми порушили основне правило мови Паскаль: тип
POINTER використовується до того, як описується. Це єдиний випадок, коли
можна використовувати ім’я типу до його опису. Тільки у разі опису вказів-
никових типів допускається використання імені типу до його опису.
Для того, щоб сформувати рядок (рис. 6) або слово, необхідно описати
дві вказівникові змінні:
VSLOV – вказівник на слово,
VLAN – вказівник на ланку (окремий елемент).
' А'
VSLOV
* а)
NIL
' А'
VSLOV
VLAN
* NIL
б)
VLAN.NEXT
' А'
VSLOV
VLAN
* NEXT
в)
VLAN.NEXT
VLAN
* NEXT NIL
г)
VLAN
д)
11
Вказівник VSLOV завжди буде вказувати на початок слова. При по-
родженні:
NEW (VSLOV);
у пам’яті машини виділяється місце для розміщення динамічної
структури з двома полями:
VSLOV.ELEM
VSLOV.NEXT
У поле ELEM – запишемо першу літеру слова, у поле NEXT – запишемо
NIL, оскільки вказівник на слово вказує лише на початок слова та з іншими
ланцюгами не зв’язаний (рис. 6, а).
Наступну, другу літеру слова необхідно розмістити у наступній ланці
ланцюга. Але якщо ми породимо знову VSLOV, то це буде вже нове слово і
зв’язку із літерою “А” не буде.
Використати вказівник VSLOV вже не можна, по-перше, цей вказівник
повинен вказувати завжди на початок слова, по-друге, літера “А” у цьому
випадку стане недоступна.
Якщо породити VLAN командою: NEW (VLAN); то втратиться
зв’язок з першою ланкою, де записана літера “А”. Тому для задання вказівника
на біжучу ланку використовується вказівник VLAN та у перший момент він
буде вказувати на першу літеру слова, тобто (рис. 6, б)
VLAN := VSLOV;
Тепер, у разі породження нового динамічного об’єкта:
NEW (VLAN.NEXT);
у поле NEXT елемента VLAN запишеться адреса наступного елемента,
тобто елемента, де буде записано другу літеру слова (рис. 6, в).
Записуємо у поле породженого елемента VLAN.NEXT значення поля
ELEM літеру “Л”, а у поле NEXT – значення NIL (оскільки невідомо, чи будуть
ще наступні ланки, див. рис. 6, г):
VLAN.NEXT.ELEM :=SYM;
VLAN.NEXT.NEXT :=NIL;
Наступний об’єкт, який повинен породжуватися VLAN.NEXT.NEXT.
Але, щоб не повторювати постійно слово NEXT, доцільно використати оператор:
VLAN := VLAN.NEXT;
Тобто перенести вказівник VLAN на наступну ланку (рис. 6, д).
Породження наступної ланки може виконуватися у циклі, де оператор:
12
VLAN := VLAN.NEXT;
аналогічний оператору I :=I +1 для подання рядка за допомогою вектора.
Фрагмент програми мовою Паскаль для формування слова має такий вигляд
(у разі набору з клавіатури символ вертикальної стрілки позначається ^ ):
PROGRAM DINSTR (INPUT, OUTPUT);
TYPE
ZV = POINTER;
POINTER = RECORD
ELEM : CHAR;
NEXT : ZV
END;
VAR
VLAN, VSLOV : ZV;
SYM : CHAR;
BEGIN
READ(SYM);
NEW (VSLOV);
VSLOV.ELEM := SYM;
VSLOV.NEXT := NIL;
VLAN := VSLOV;
WHILE SYM <> ’.’ DO
BEGIN
READ(SYM);
NEW(VLAN.NEXT);
VLAN := VLAN.NEXT;
VLAN.ELEM := SYM;
VLAN.NEXT := NIL;
END;
END.
Приклад: Сформувати рядок, що закінчується крапкою, і порахувати
скільки разів у нього входить літера “А”.
Текст програми мовою Паскаль (де – символ пробілу; у разі набору з
клавіатури символ вертикальної стрілки позначається ^ ):
PROGRAM KBUK (INPUT, OUTPUT);
CONST BUK = ’A’;
TYPE
ZV = POINTER;
13
POINTER = RECORD
ELEM : CHAR;
NEXT : ZV
END;
VAR
VLAN, VSLOV : ZV;
SYM : CHAR;
K : INTEGER;
BEGIN
WRITELN (’ВВЕДИ СИМВОЛИ ДО КРАПКИ’);
READ(SYM);
NEW(VSLOV);
VSLOV.ELEM := SYM;
VSLOV.NEXT := NIL;
VLAN := VSLOV;
REPEAT
READ(SYM);
NEW(VLAN.NEXT);
VLAN := VLAN.NEXT;
VLAN.ELEM := SYM;
VLAN.NEXT := NIL
UNTIL SYM = ’.’;
VLAN := VSLOV; {ПОВЕРНЕННЯ НА ПОЧАТОК РЯДКА}
WHILE VLAN.NEXT <> NIL DO
{ПЕРЕХІД ВІД ЛАНКИ ДО ЛАНКИ ТА ПОШУК ЛІТЕРИ}
BEGIN
IF VLAN.ELEM = BUK THEN K :=K+1;
VLAN := VLAN.NEXT
END;
WRITELN;
{ВВИВІД РЯДКА}
VLAN := VSLOV; {ПОВЕРНЕННЯ НА ПОЧАТОК РЯДКА}
WHILE VLAN.NEXT <> NIL DO
{ПЕРЕХІД ВІД ЛАНКИ ДО ЛАНКИ ТА ВИВІД ЛІТЕРИ ЛАНКИ}
BEGIN
WRITE (VLAN.ELEM);
VLAN := VLAN.NEXT;
END;
WRITELN;
14
WRITELN(’ЛІТЕРА А ВХОДИТЬ У РЯДОК ’,K, ’ РАЗІВ’);
END.
15
Лабораторна робота № 13
ДИНАМІЧНІ ОБ’ЄКТИ
СКЛАДНОЇ СТРУКТУРИ.
ОДНО- ТА ДВОНАПРАВЛЕНІ
СПИСКИ, СТЕКИ, ЧЕРГИ
1. МЕТА РОБОТИ
Мета роботи – ознайомитись із особливостями застосування динамічних
об’єктів складної структури: списками, стеками та чергами; з операціями, які
виконуються над елементами цих об’єктів. Набути практичних навичок
програмування з використанням динамічних об’єктів складної структури.
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
2
Q :=Q.NEXT
END;
POSH := L
END;
а) a)
LANKA.NEXT
LANKA.NEXT.NEXT
LANKA
'А' 'B' 'C'
'S'
NEXT
б)
б)
3
у поле NEXT попереднього елемента “А” внести вказівник на елемент, що
вставляється.
Цей алгоритм відображається процедурою:
PROCEDURE VST(LANKA : ZV; BUK : CHAR);
VAR Q : ZV;
BEGIN
NEW (Q);
Q.ELEM := BUK;
Q.NEXT := LANKA.NEXT;
LANKA.NEXT := Q
END;
LANKA
'А ' 'B '
LANKA.NEXT
'C '
LANKA.NEXT.NEXT
А B C
LANKA LANKA.NEXT.NEXT
Q
NEXT.NEXT NEXT NEXT
А ' ' ' '
B '
C '
NEXT.NEXT NEXT
б) NEXT
б) б)
Рис. 2. Алгоритм видалення елемента з динамічного рядка
4
PROCEDURE VUDAL (LANKA : ZV);
VAR Q : ZV;
BEGIN
Q := LANKA.NEXT;
LANKA.NEXT := LANKA.NEXT.NEXT;
DISPOSE(Q)
END;
Щоб не розірвати ланцюжок, слід спочатку запам’ятати об’єкт, який
видаляється під іменем Q, тоді перенести вказівник на наступну ланку і
лише після цього видалити об’єкт Q. Якщо такий порядок буде порушений,
то неминучий розрив ланцюжка.
5
Q.ELEM:=BUK;
Q.NEXT:=LANKA.NEXT;
LANKA.NEXT:=Q
END;
6
VLAN:=VLAN.NEXT;
REPEAT
IF VLAN.ELEM=’R’ THEN
VSTAV(VLAN,’1’);
IF VLAN.ELEM=VLAN.NEXT.ELEM THEN
VYDAL(VLAN);
VLAN:=VLAN.NEXT
UNTIL VLAN.NEXT=NIL;
{ВИВIД РЕЗУЛЬТАТУ}
WRITELN(’РЕЗУЛЬТУЮЧИЙ РЯДОК’);
VLAN:=VSLOV;
VLAN:=VLAN.NEXT;
REPEAT
WRITE(VLAN.ELEM);
VLAN:=VLAN.NEXT
UNTIL VLAN=NIL;
WRITELN;
END.
7
1 2 1 3
Початок
14
2 VKGR.ELEM= ні
Формування VKB.ELEM
заголовку
так
3 15
VKB = VSLOV K=K+1
16
4 Ввід VKB=VKB.NEXT
SYM
17
5 VUDAL
VSTAV
6
VKB =
VKB.NEXT 18
K = ...
ні 7 1 2 9
SYM = '.'
19 20 27
так SYM='1' SYM='2' ... SYM='9'
8
Перехід на
початок 28
VSTAV
29
9 ні VK=VKGR
VKB.ELEM '.'
30
VKGR=VKGR.NEXT
10 так
Вивід
рядка 31
VKB Перехід на
початок
11
Перехід на 32
початок VKB.ELEM '.' ні
так
33
12 ні VK=VK.NEXT
VKGR.ELEM
'.'
34
Вивід
так VK.ELEM
13 35
k =1 Кінець
2 1 3
8
Блок 6 – встановлення біжучого вказівника VKB на утворений наступний
ланцюжок.
Блок 7 – перевірка умови закінчення циклу на наявність введеного символу
крапки.
Блок 8 – встановлення біжучого вказівника VKB на початок рядка.
Блоки 9, 10 – циклічне виведення сформованого рядка VKB.
Блок 9 – перевірка умови виконання циклу, поки нема символу крапки
(ознаки кінця рядка)
Блок 10 – встановлення біжучого вказівника VKB на наступний ланцюжок.
Блок 11 – встановлення біжучого вказівника VK на початок рядка і вказів-
ника на групу однакових літер VKGR на першу літеру рядка VK.NEXT.
Блоки 12–30 – цикл для визначення кількості однакових літер, їх видалення
та запису цифри перед кожною групою літер.
Зокрема:
Блоки 14–17 – цикл для обчислення кількості однакових літер у групі та
видалення однакових літер з групи.
Блок 14 – перевірка умови збігання значення двох полів ELEM вказівників.
Блок 15 – зростання лічильника кількості на одиницю.
Блок 16 – встановлення біжучого вказівника VKB на наступний ланцюжок.
Блок 17 – виклик підпрограми видалення елемента динамічного рядка.
Блоки 18–27 – перетворення порахованої кількості К однакових літер з
цифри у символ цифри (оскільки рядок – це динамічний об’єкт, елементами
якого є символи).
Блок 28 – вставка цифри, що відображає кількість однакових літер перед
групою.
Блок 29 – запис літери у рядок VK.
Блок 30 – перехід на наступну групу.
Блок 31 – перехід на початок динамічного об’єкта VK.
Блоки 32–34 – друк сформованого рядка VK.
Блок 32 – перевірка умови виконання циклу, поки нема символу крапки
(ознаки кінця рядка)
Блок 33 – встановлення біжучого вказівника VKB на наступний ланцюжок.
Блок 34 – вивід на екран значення поля ELEM біжучого вказівника VKB.
Блок 35 – кінець алгоритму.
У програмі будуть використані чотири змінні вказівникового типу:
VSLOV – вказівник на початок рядка;
VKB – біжучий вказівник;
VKGR – вказівник на початок групи однакових літер;
VK – вказівник на ланку, після якої вставляється цифра.
9
Текст програми мовою Паскаль (у разі набору з клавіатури символ
вертикальної стрілки позначається ^ ):
PROGRAM DINSTR (INPUT, OUTPUT);
TYPE
ZV = POINTER;
POINTER = RECORD
ELEM : CHAR;
NEXT :ZV
END;
VAR
VSLOV,VKB,VKGR,VK :ZV;
K :1..9;
SYM : CHAR;
10
VSTAV(VKB, SYM);{ВИКЛИК ПРОЦЕДУРИ
ВСТАВКИ}
VKB := VKB.NEXT
UNTIL SYM = ’.’;
{ВИВІД СФОРМОВАНОГО РЯДКА}
VKB := VSLOV;
WHILE VKB.ELEM <> ’.’ DO
BEGIN
WRITE(VKB.ELEM);
VKB := VKB.NEXT
END;
{ФОРМУВАННЯ ЛАНЦУЖКА VK ГРУПИ ОДНАКОВИХ ЛІТЕР}
VK := VSLOV;
VKGR := VK.NEXT;
WHILE VKGR.ELEM <> ’.’ DO
BEGIN
K :=1;
VKB := VKGR.NEXT;
WHILE VKGR.ELEM = VKB.ELEM DO
BEGIN
K := K+1;
VKB := VKB.NEXT;
VUDAL(VKGR) {ВИКЛИК ПРОЦЕДУРИ
ВИДАЛЕННЯ}
END;
CASE K OF
1 : SYM := ’1’;
2 : SYM := ’2’;
3 : SYM := ’3’;
4 : SYM := ’4’;
5 : SYM := ’5’;
6 : SYM := ’6’;
7 : SYM := ’7’;
8 : SYM := ’8’;
9 : SYM := ’9’
END;
VSTAV(VK, SYM); {ВИКЛИК ПРОЦЕДУРИ ВСТАВКИ}
VK := VKGR; {ПЕРЕХІД ДО НАСТУПНОЇ ГРУПИ}
VKGR := VKGR.NEXT;
11
END;
{ВИВІД СФОРМОВАНОГО РЯДКА VK}
VK := VSLOV;
WHILE VK.ELEM <> ’.’ DO
BEGIN
VK := VK.NEXT;
WRITE(VK.ELEM)
END;
READLN
END.
12
разі створення такого списку в запис додається ще одне поле – PRIV –
вказівник на попередній елемент. Схематично це зображено на рис. 4:
VSTR
13
VSTR
NEXT NEXT NEXT ... NEXT
* PRIV PRIV
ELEM
PRIV
ELEM
... PRIV
ELEM
VSTR
NEXT NEXT NEXT ... NEXT
* NIL NIL PRIV
ELEM
PRIV
ELEM
... PRIV
ELEM
14
ST – вказівник на рядок;
BUK – символ або елемент, який шукається;
REZ – параметр-змінна через який передається вказівник на перше
входження заданого символу.
LANKA.NEXT
LANKA
A B C
NEXT
PRIV
15
2.3.2. ПРОЦЕДУРА ВИДАЛЕННЯ ЗАДАНОГО ЕЛЕМЕНТА
З ДВОНАПРАВЛЕНОГО СПИСКУ
а)
а)
а)
LANKA
LANKA.NEXT
LANKA.PRIV
LANKA
NEXT NEXT LANKA.NEXT
NEXT
LANKA.PRIV
б) б)
16
Для рядка символів, що складається з літер і цифр, починається з літери
“А” і вводиться до появи крапки, видалити усі цифри, які зустрічаються перед
літерою “D”.
Приклад тестового рядка:
ABS357D45L54IK794DS7K.
Результатом буде рядок:
ABSDS45L54IKDS7K
Текст програми:
PROGRAM DVONAPR(INPUT, OUTPUT);
TYPE ZV2 = POINT2;
POINT2 = RECORD
ELEM : CHAR;
NEXT : ZV2;
PRIV : ZV2
END;
VAR
VSLOV, VLAN, VLAN1 : ZV2;
SYM : CHAR;
17
Q := LANKA;
LANKA.NEXT.PRIV := LANKA.PRIV;
LANKA.PRIV.NEXT := LANKA.NEXT;
DISPOSE(Q)
END;
{ГОЛОВНА ПРОГРАМА}
BEGIN
{ФОРМУВАННЯ ЛАНКИ-ЗАГОЛОВКУ}
NEW(VSLOV);
VSLOV.ELEM := ’A’;
VSLOV.NEXT := VSLOV;
VSLOV.PRIV := VSLOV;
VLAN := VSLOV;
{ФОРМУВАННЯ ДВОНАПРАВЛЕНОГО СПИСКУ}
REPEAT
READ(SYM);
VSTAV(VLAN.PRIV, SYM) {ВИКЛИК ПРОЦЕДУРИ
ВСТАВКИ}
UNTIL SYM = ’.’;
{ПОВЕРНЕННЯ ПО КІЛЬЦЮ НА ПОЧАТОК СПИСКУ}
VLAN := VSLOS.NEXT;
WHILE VLAN <> VSLOV DO
BEGIN
WHILE (VLAN.ELEM <> ’D’) AND (VLAN <>
VSLOV) DO
VLAN := VLAN.NEXT;
IF VLAN <> VSLOV THEN
BEGIN
WHILE VLAN.PRIV.ELEM IN [’0’..
’9’]DO
VUDAL(VLAN.PRIV);
{ВИКЛИК ПРОЦЕДУРИ}
VLAN := VLAN.NEXT
END;
END;
{ВИВІД РЕЗУЛЬТАТІВ}
VLAN := VSLOV.NEXT;
18
WRITELN;
WHILE VLAN <> VSLOV DO
BEGIN
WRITE(VLAN.ELEM);
VLAN := VLAN.NEXT
END;
WRITELN
END.
19
який прийшов останнім.
Черга такого типу називається стеком. Вона найчастіше
використовується у програмуванні.
У стека доступна єдина позиція – вершина стека – тобто позиція, в якій
перебуває останній елемент. Вибрати елемент можна лише з вершини стека,
при цьому вибраний елемент видаляється зі стека.
Стек найчастіше зображується у вигляді динамічної структури, одно-
направленого списку (ланцюжка). Вершиною стека є перша ланка. Ланка-заго-
ловок для стека не потрібна.
Вказівник на стек є вказівником вершини стека. Вказівник на ланку
містить вказівник на наступну ланку стека. “Дно” стека, тобто елемент, занесе-
ний найпершим, містить вказівник NIL.
Схематично стек зображається:
STEK
* EL EL EL
...
EL
Опишемо тип:
TYPE
ZV = POINTER;
POINTER = ROCORD
ELEM : CHAR;
NEXT : ZV
END;
VAR
STEK : ZV;
. . .
20
Процедура має два формальні параметри: вказівник на стек ST і символ,
що заноситься BUK.
PROCEDURE VSTEK(VAR ST : ZV; BUK : CHAR);
VAR Q : ZV;
BEGIN
NEW(Q);
Q.ELEM := BUK;
Q.NEXT := ST;
ST := Q
END;
21
ST := ST.NEXT;
DISPOSE(Q)
END
END;
У цій процедурі обов’язково потрібно використати додаткову змінну Q,
де запам’ятати вказівник на ланку, яка знищується. Записати DISPOSE(ST) не
можна, оскільки знищиться весь стек.
22
{ПРОЦЕДУРА ВИДАЛЕННЯ ЕЛЕМЕНТА ІЗ СТЕКА}
PROCEDURE VDSTEK(VAR ST:ZV;VAR A:CHAR; VAR L:BOOLEAN);
VAR Q : ZV;
BEGIN
L := TRUE;
IF ST = NIL THEN L := FALSE
ELSE
BEGIN
A := ST.ELEM;
Q := ST;
ST := ST.NEXT;
DISPOSE(Q)
END
END;
{ГОЛОВНА ПРОГРАМА}
BEGIN
S := NIL;
READ(SYM);
B := TRUE;
WHILE (SYM <> ’.’) AND B DO
BEGIN
IF SYM = ’(’ THEN VSTEK(S, SYM)
ELSE
IF SYM = ’)’ THEN
VDSTEK(S, K, B);
READ(SYM);
END;
IF (S = NIL) AND B THEN WRITELN(’БАЛАНС ДУЖОК Є’)
ELSE WRITELN(’БАЛАНСУ ДУЖОК НЕМА’);
END.
23
Лабораторна робота № 14
ДИНАМІЧНІ ОБ’ЄКТИ
СКЛАДНОЇ СТРУКТУРИ.
ДИНАМІЧНІ ТАБЛИЦІ, БІНАРНІ
ДЕРЕВА
1. МЕТА РОБОТИ
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
2.1. ТАБЛИЦІ
2
1) ключ;
2) текст запису;
3) вказівник на наступну ланку.
TAB
1 2 3 N
3
Бінарне дерево можна зобразити так (рис. 2): маємо набір вершин, з
кожної вершини виходить не більше як дві стрілки (гілки), спрямовані
вліво-вниз та вправо-вниз. Існує одна-єдина вершина, в яку не входить
жодна стрілка, ця вершина називається коренем дерева. У всі інші вершини
входить лише одна стрілка.
* *
* *
* *
* * * *
Рис. 2. Бінарне дерево
70
LV
PR
ZAP
62 78
LV LV
PR PR
ZAP ZAP
60
72 80
LV
LV LV
PR
PR PR
ZAP
ZAP ZAP
4
Кожна ланка бінарного дерева буде записом з чотирма полями:
1. Ключ запису – KLYTH;
2. PR – вказівник на вершину вправо-вниз;
3. LV – вказівник на вершину вліво-вниз;
4. ZAP – вказівник на текст запису.
Опишемо тип:
TYPE
VKTEK = CHAR;
VKL = POINTER3;
POINTER3 = RECORD
KLUCH : INTEGER;
PR, LV : VKL;
ZAP : VKTEK
END;
Розглянемо принцип побудови бінарного дерева у разі занесення
записів у таблицю. Нехай в таблицю заносяться записи з ключами 70, 62, 78,
72, 60, 80, 90, 40, 30, 77, 64, 20, 86. Коренем дерева буде перший запис з кодом
70, вказівники на наступні елементи поки що зробимо пустими, тобто NIL.
Якщо ключ наступного запису К2< K1 менше попереднього (К1 = 70; К2 = 62;
К2 < K1), то він записується в ліву вершину; якщо К2 > K1, то він записується у
праву вершину. При К3=78, то К3 > K1, то формується вершина справа (рис. 4).
70
62 78
72 80
60 64
40 77 90
86
30
20
5
2. Починаючи з кореня дерева, порівнюємо ключ К з ключем чергової
вершини, якщо К > Квер , то переходимо праворуч; якщо К < Квер , то
переходимо ліворуч від цієї вершини.
3. Якщо стрілки від вершини немає, то приєднюється до цієї вершини
нова вершина з ключем К.
ELSE
P
:= P.PR
UNTIL B OR (P = NIL);
PDER := B;
REZ := P
END;
6
правого послідовника. Для здійснення пошуку таким методом необхідно буде
перебрати log2N вершин.
7
Процедура вставки запису в таблицю, представлену бінарним деревом,
буде мати вигляд:
PROCEDURE VTABL(K : INTEGER; VAR D : VKL; REC : CHAR);
VAR
R, S : VKL;
T : VKTEK;
BEGIN
IF NOT PDER2(K, D, R) THEN
BEGIN
NEW(T);
T := REC;
NEW(S);
S.KLYСH := K;
S.ZAP := T;
S.LV := NIL;
S.PR := NIL;
IF D = NIL THEN D := S
ELSE
IF K <>
R.KLUCH THEN
R.LV := S
ELSE
R.PR :=S
END
END;
8
40
35 35
32
32
а)
а) б)б)
21 100 21 100
13 120 13 35 120
40
150 50 150
30 50 30
27 35 27 60
60 32
а)
32 а) б)
• б)
9
1. Ланки із заданим ключем немає.
2. Ланка із заданим ключем має не більше однієї гілки.
3. Ланка із заданим ключем має 2 гілки.
Використаємо допоміжну рекурсивну процедуру UD для третього випадку.
У ній здійснюється “спуск” до крайнього правого елемента лівого піддерева
елемента Q, що видаляється. Значення поля KLUCH та поля ZAP елемента Q
замінюється відповідними значеннями полів KLUCH та ZAP елемента R.
PROCEDURE UDALDER(VAR D : VKL; K : INTEGER);
VAR
Q : VKL;
10
ELSE
IF Q.LV = NIL THEN
D := Q.PR
ELSE
UD(Q.LV)
END
END;
11
дерева.
Блоки 5, 6 – формування дерева за допомогою процедури VTABL.
Блок 7 – присвоювання вказівникам ST1 та ST2 значення NIL. Вказівник
ST1 використовується під час пошуку в дереві, ST2 – під час формування стека
“двієчників”.
Блок 8 – присвоювання біжучому вказівнику P значення D – кореня
дерева.
Блоки 9–27 – обробка таблиці, представленої у вигляді дерева, поки
біжучий вказівник Р не дорівнюватиме NIL.
Блоки 10–12 – обчислення суми оцінок кожного студента Si.
Блоки 13–15 – друк прізвища відмінника, якщо сума його оцінок Si = 25.
Блоки 16, 17 – занесення у стек ST2 прізвища студента-“двієчника”, у
яких Si10.
Блок 18 – перевірка, чи не досягли кінця дерева, тобто, чи права та ліва
гілки дерева не дорівнюють NIL.
12
1
Поч аток
2
i =1, 500
4
D = NIL
3
Ввід
інформації 5
i =1, 500
6
VTABL
7
ST1 = NIL
ST2 = NIL
8
P=D
9 ні
P NIL
так
10
Si = 0
11
i = 1, 5
12
Si = Si + OC
13
Si =25
ні
так 14
Вивід
16 ні
Si 10
відмінників
15 17 так
VSTEK
PRINT
ST2
2 1 3
13
NIL
NIL
NIL
NIL
PR
NIL
двієчників
14
Блок 19 – якщо не досягли, то заносимо праву гілку у стек ST1.
Блок 20 – перехід на ліву гілку.
Блок 21 – якщо досягнуто кінця дерева, то виконується блок 22.
Блок 22 – перевірка, чи стек ST1 порожній.
Блок 23 – присвоювання біжучому вказівнику Р значення NIL, якщо не
порожній, то виконується блок 24
Блок 24 – видалення із стека ST1.
Блоки 25-27 – рух по дереву вправо або вліво.
Блоки 28-31 – друк прізвища студента-“двієчника” зі стека ST2.
Блок 32 – кінець алгоритму.
У програмі використані процедури:
VSTEK – запис у стек; формальні параметри: ST – ім’я стека (ST1, ST2)
та NEL – інформація, що заноситься у стек.
UDSTEK – видалення зі стека; формальні параметри: ST – ім’я стека
(ST1, ST2); А – інформація, що видаляється зі стека (у цьому випадку – прізви-
ще студента).
VTABL – занесення вузла дерева; формальні параметри: К – ключ, що є
прізвищем студента; D – вказівник на вершину дерева; REC – інформація про
студента, тобто шифр групи та оцінки.
PRINT – друк прізвища студента; формальний параметр: Р – вказівник
на вузол дерева.
У програмі також використано функцію пошуку вузла з відповідним
ключем у дереві: PDER; формальними параметрами є: D – вказівник на корінь
дерева; REZ – вказівник на вузол, у якому знайдено відповідний ключ. Функція
PDER повертає результат логічного типу: TRUE – якщо вузол знайдено, та
FALSE – якщо вузол не знайдено.
Текст програми мовою Паскаль:
PROGRAM DERUSP (INPUT, OUTPUT);
CONST M = 5; N = 10;
TYPE
PRIZV = ARRAY [1..N] OF CHAR;
VKTEK = TEKST;
TEKST = RECORD
GR : 11..59;
OC : ARRAY [1..M] OF 2..5
END;
VKL = POINTER3;
POINTER3 = RECORD
15
KLUCH : PRIZV;
LV, PR : VKL;
ZAP : VKTEK
END;
TELST = VKL;
ZVST = POINTER;
POINTER = RECORD
ELEM : TELST;
NEXT : ZVST
END;
INFORM = RECORD
FM : PRIZV;
INF : TEKST
END;
VAR
STUD : FILE OF INFORM;
STUDENT : ARRAY[1..500] OF INFORM;
ST1, ST2 : ZVST;
Q1 : VKTEK;
P, D : VKL;
FAM : PRIZV;
SI, I, J : INTEGER;
{ПРОЦЕДУРА ЗАПИСУ У СТЕК}
PROCEDURE VSTEK (VAR ST : ZVST; NEL : TELST);
VAR Q : ZVST;
BEGIN
NEW(Q);
Q.ELEM := NEL;
Q.NEXT := ST;
ST := Q
END;
{ПРОЦЕДУРА ВИДАЛЕННЯ ЗІ СТЕКУ}
PROCEDURE UDSTEK(VAR ST : ZVST; VAR A : TELST);
VAR Q : ZVST;
BEGIN
A := ST.ELEM;
Q := ST;
ST := ST.NEXT;
DISPOSE(Q);
16
END;
{ФУНКЦІЯ ПОШУКУ В ДЕРЕВІ}
FUNCTION PDER2(K : PRIZV; VAR D, REZ : VKL): BOOLEAN;
VAR P, Q : VKL; B : BOOLEAN;
BEGIN
B := FALSE;
P := D;
IF D <> NIL THEN
REPEAT
Q := P;
IF P.KLUCH = K THEN
B := TRUE
ELSE
BEGIN
Q := P;
IF K < P.KLUCH THEN
P := P.LV
ELSE
P := P.PR
END
UNTIL B OR (P = NIL);
PDER2 := B;
REZ := Q
END;
{ПРОЦЕДУРА ВСТАВКИ}
PROCEDURE VTABL(k : PRIZV; VAR D : VKL; REC : TEKST);
VAR R, S : VKL; I : INTEGER; T : VKTEK;
BEGIN
IF NOT PDER2(K,D,R) THEN
BEGIN
NEW(T);
T := REC;
NEW(S);
S.KLUCH : =K;
S.LV := NIL; S.PR := NIL;
IF D = NIL THEN D := S
ELSE
IF K < R.KLUCH
THEN R.LV := S
17
ELSE R.PR := S
END
END;
{ПРОЦЕДУРА ВИВОДУ}
PROCEDURE PRINT (R : VKL);
VAR I : INTEGER;
BEGIN
FOR I := 1 TO N DO
WRITE(R.KLYTH[I]);
WRITELN
END;
{ГОЛОВНА ПРОГРАМА}
BEGIN
ASSIGN (STUD, ’STUD.DAT’);
RESET(STUD);
D := NIL;
FOR I := 1 TO 500 DO
READ(STUD, STUDENT[I]);
FOR I := 1 TO 500 DO
VTABL(STUDENT[I].FM, D, STUDENT[I].INF);
ST1 := NIL; ST2 := NIL;
P := D;
WHILE P <> NIL DO
BEGIN
SI := 0;
FOR I := 1 TO M DO
SI := SI + P.ZAP.OC[I];
IF SI = 25 THEN
BEGIN
WRITE(’ВІДМІННИК’);
PRINT(P)
END
ELSE
IF SI <= 10 THEN
VSTEK(ST2,P);
IF (P.PR <> NIL) AND ( P.LV <> NIL)
THEN
BEGIN
VSTEK(ST1, P.PR);
P := P.LV
18
END
ELSE
IF (P.PR = NIL) AND (P.LV = NIL) THEN
BEGIN
IF ST1 = NIL THEN P
:= NIL
ELSE UDSTEK(ST1,P)
END
ELSE
BEGIN
IF P.PR =
NIL THEN P := P.LV
ELSE P := P.PR
END
END;
WRITELN(’СПИСОК ДВІЙОЧНИКІВ’);
WHILE ST2 <> NIL DO
BEGIN
UDSTEK(ST2, P);
PRINT(P)
END
END.
19