SlideShare a Scribd company logo
1 of 133
Динамічні структури
даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
1. Вказівники
2. Динамічні
масиви
3. Структури
4. Списки
5. Стеки, черги,
деки
6. Дерева
7. Графи
Тема 1. Вказівники
Динамічні структури
даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
3
Статині дані
• змінна (масив) має ім'я, за яким до неї можна
звертатися
• розмір наперед відомий (задається при
написанні програми)
• пам'ять виділяється при оголошенні
• розмір не можна збільшити під час роботи
програми
var x, y: integer;
z: real;
A: array[1..10] of real;
str: string;
var x, y: integer;
z: real;
A: array[1..10] of real;
str: string;
4
Динамічні дані
• розмір наперед невідомий, визначається під час
роботи програми
• пам'ять виділяється під час роботи програми
• немає імені?
Проблема:
як звертатися до даних, якщо немає імені?
Рішення:
використовувати адресу в пам'яті
Наступна проблема:
в яких змінних може міститися адреса?
як працювати з адресами?
5
Вказівники
Вказівник – це змінна, в яку можна записати адресу
другої змінної (або блоку пам'яті).
Оголошення:
Як записати адресу:
var pC: ^char; // адреса символу
pI: ^integer; // адреса цілої змінної
pR: ^real; // адреса дійсн. змінної
var pC: ^char; // адреса символу
pI: ^integer; // адреса цілої змінної
pR: ^real; // адреса дійсн. змінної
var m: integer; // ціла змінна
pI: ^integer; // вказівник
A: array[1..2] of integer; // масив
...
pI:= @ m; // адреса змінної m
pI:= @ A[1]; // адреса елемента масиву A[1]
pI:= nil; // нульова адреса
var m: integer; // ціла змінна
pI: ^integer; // вказівник
A: array[1..2] of integer; // масив
...
pI:= @ m; // адреса змінної m
pI:= @ A[1]; // адреса елемента масиву A[1]
pI:= nil; // нульова адреса
@
^
nil
вказівниквказівник
адреса коміркиадреса комірки
6
Звернення до даних через вказівник
program qq;
var m, n: integer;
pI: ^integer;
begin
m := 4;
pI := @m;
writeln('Адрес m = ', pI);//виведення адреси
writeln('m = ', pI^); // виведення значення
n := 4*(7 - pI^); // n = 4*(7 - 4) = 12
pI^ := 4*(n - m); // m = 4*(12 – 4) = 32
end.
program qq;
var m, n: integer;
pI: ^integer;
begin
m := 4;
pI := @m;
writeln('Адрес m = ', pI);//виведення адреси
writeln('m = ', pI^); // виведення значення
n := 4*(7 - pI^); // n = 4*(7 - 4) = 12
pI^ := 4*(n - m); // m = 4*(12 – 4) = 32
end.
^
«витягнути»
значення за адресою
«витягнути»
значення за адресою
7
Звернення до даних (масиви)
program qq;
var i: integer;
A: array[1..4] of integer;
pI: ^integer;
begin
for i:=1 to 4 do A[i] := i;
pI := @A[1]; // адреса A[1]
while ( pI^ <= 4 ) // while( A[i] <= 4 )
do begin
pI^ := pI^ * 2; // A[i] := A[i]*2;
pI := pI + 1; // до наступного елемента
end;
end.
program qq;
var i: integer;
A: array[1..4] of integer;
pI: ^integer;
begin
for i:=1 to 4 do A[i] := i;
pI := @A[1]; // адреса A[1]
while ( pI^ <= 4 ) // while( A[i] <= 4 )
do begin
pI^ := pI^ * 2; // A[i] := A[i]*2;
pI := pI + 1; // до наступного елемента
end;
end.
переміститися до наступного
елементу = змінити адресу
на sizeof(integer)
переміститися до наступного
елементу = змінити адресу
на sizeof(integer)
Не працює в
PascalABC.NET!
Не працює в
PascalABC.NET!
!!
8
Що потрібно знати про вказівники
• вказівник – це змінна, в якій можна зберігати адресу іншої
змінної;
• при оголошенні вказівника потрібно вказувати тип змінної,
на який він буде вказувати, а перед типом поставити
знак ^ ;
• знак @ перед іменем змінної означає її адресу;
• запис p^ означає значення комірки, на яку вказує
вказівник p;
• nil – це нульовий вказівник, він нікуди не показує
• при зміні значення вказівника на n він насправді зміщується
до n-го наступного числа даного типу (для вказівника на
цілі числа – на n*sizeof(integer) байт).
Не можна використовувати вказівники, які показують
невідомо куди (буде збій або зависання)!
Тема 2. Динамічні масиви
Динамічні
структури даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
10
Де потрібні динамічні масиви?
Задача. Ввести розмір масиву, потім – елементи масиву.
Відсортувати масив і вивести на екран.
Проблема:
розмір масиву наперед невідомий.
Шляхи розв'язання:
1) виділити пам’ять «з запасом»;
2) виділяємо пам’ять тоді, коли розмір став відомий.
Алгоритм:
1) ввести розмір масиву;
2) виділити пам’ять;
3) ввести елементи масиву;
4) відсортувати і вивести на екран;
5) знищити масив.
виділити пам’ятьвиділити пам’ять
знищити масивзнищити масив
11
Використовування вказівників (Delphi)
program qq;
type intArray = array[1..1] of integer;
var A: ^intArray;
i, N: integer;
begin
writeln('Розмір масиву>');
readln(N);
GetMem(pointer(A), N*sizeof(integer));
for i := 1 to N do
readln(A[i]);
... { сортування }
for i := 1 to N do
writeln(A[i]);
FreeMem(pointer(A));
end.
виділяємо пам’ятьвиділяємо пам’ять
вивільняємо пам’ятьвивільняємо пам’ять
працюємо так
само, як з
звичайним
масивом!
працюємо так
само, як з
звичайним
масивом!
деякий масив цілих чиселдеякий масив цілих чисел
12
Використання вказівників
• для виділення пам’яті використовується процедура
GetMem
GetMem( вказівник, розмір в байтах );
• вказівник повинен бути прив'язаний до типу pointer–
вказівник без типу, просто адреса деякого байта в
пам'яті;
• з динамічним масивом можна працювати так само, як і
з звичайним (статичним);
• для вивільнення блоку пам'яті потрібно застосувати
процедуру FreeMem:
FreeMem ( вказівник );
13
Помилки при роботі з пам’ятю
Запис в «чужу» область пам'яті:
пам'ять не була виділена, а масив використовується.
Що робити: так не робити.
Вихід за границі масиву:
звернення до елементу масиву з неправильним номером, при
запису псуються дані в «чужій» пам’яті.
Що робити: якщо дозволяє транслятор, включати перевірку
виходу за границі масиву.
Вказівник знищується другий раз:
структура пам’яті порушена, може бути все, що завгодно.
Що робити : в видалений вказівник краще записати nil,
помилка виявиться швидше.
Витік пам'яті:
непотрібна пам’ять не вивільняється.
Що робити : прибирайте «сміття»
(в середовищі .NET є збирач сміття!)
14
Динамічні масиви (Delphi)
program qq;
var A: array of integer;
i, N: integer;
begin
writeln(‘Розмір масиву>');
readln(N);
SetLength ( A, N );
for i := 0 to N-1 do
readln(A[i]);
... { сортування }
for i := 0 to N-1 do
writeln(A[i]);
SetLength( A, 0 );
end.
виділяємо пам’ятьвиділяємо пам’ять
вивільняємо пам’ятьвивільняємо пам’ять
деякий масив
цілих чисел
деякий масив
цілих чисел
нумеруємо з НУЛЯ!нумеруємо з НУЛЯ!
15
Динамічні масиви (Delphi)
• при оголошенні масиву вказується тільки його тип,
пам’ять не виділяється:
var A: array of integer;
• для виділення пам’яті використовується процедура
SetLength (встановити довжину)
SetLength ( масив, розмір );
• номери елементів починаються з НУЛЯ!
• для вивільнення блоку пам’яті потрібно встановити
нульову довжину через процедуру SetLength:
SetLength ( масив, 0 );
16
Динамічні матриці (Delphi)
Задача. Ввести розмір матриці і виділити для неї місце в
пам’яті під час роботи програми.
Проблема:
розміри матриці наперед невідомі
Розв'язання:
var A: array of array of integer;
N, M: integer;
begin
writeln(‘Кількість рядків і стовпців>');
readln(N, M);
SetLength ( A, N, M );
... // працюємо, як з звичайною матрицею
SetLength( A, 0, 0 );
end.
var A: array of array of integer;
N, M: integer;
begin
writeln(‘Кількість рядків і стовпців>');
readln(N, M);
SetLength ( A, N, M );
... // працюємо, як з звичайною матрицею
SetLength( A, 0, 0 );
end.
Тема 3. Структури (записи)
Динамічні
структури даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
18
Структури (в Паскалі – записи)
Структура (запис) – це тип даних, які можуть
включати в себе декілька полів – елементів різних
типів (в том числі і інші структури).
Властивості:
• автор (рядок)
• назва (рядок)
• рік видання (ціле число)
• кількість сторінок (ціле число)
Задача: об'єднати ці
дані в одне ціле
Задача: об'єднати ці
дані в одне ціле
Розміщення в пам’яті
автор назва
рік
видання
кількість
сторінок
40 символів 80 символів ціле ціле
19
Один запис
readln(Book.author); // введення
readln(Book.title);
Book.year := 1998; // присвоєння
if Book.pages > 200 then // порівняння
writeln(Book.author,'.',Book.title);// виведення
readln(Book.author); // введення
readln(Book.title);
Book.year := 1998; // присвоєння
if Book.pages > 200 then // порівняння
writeln(Book.author,'.',Book.title);// виведення
Оголошення (виділення пам’яти):
var Book: record
author: string[40]; // автор, рядок
title: string[80]; // назва, рядок
year: integer; // рік видання, ціле
pages: integer; // кількість сторінок, ціле
end;
var Book: record
author: string[40]; // автор, рядок
title: string[80]; // назва, рядок
year: integer; // рік видання, ціле
pages: integer; // кількість сторінок, ціле
end;
названазва записзапис поляполя
Звернення до полів: Для звернення до
поля запису
використовується
крапка!
Для звернення до
поля запису
використовується
крапка!
!!
20
Масив записів
Оголошення (виділення пам’яті):
const N = 10;
var aBooks: array[1..N] of record
author: string[40];
title: string[80];
year: integer;
pages: integer;
end;
const N = 10;
var aBooks: array[1..N] of record
author: string[40];
title: string[80];
year: integer;
pages: integer;
end;
Books[1] ... Books[10]
author title year pages
21
Масив записів
for i:=1 to N do begin
readln(aBooks[i].author);
readln(aBooks[i].title);
...
end;
for i:=1 to N do
if aBooks[i].pages > 200 then
writeln(aBooks[i].author, '.',
aBooks[i].title);
for i:=1 to N do begin
readln(aBooks[i].author);
readln(aBooks[i].title);
...
end;
for i:=1 to N do
if aBooks[i].pages > 200 then
writeln(aBooks[i].author, '.',
aBooks[i].title);
Звернення до поля:
aBooks[i].author – звернення до поля
author запису aBooks[i]
aBooks[i].author – звернення до поля
author запису aBooks[i]
!!
22
Новий тип даних – запис
const N = 10;
var Book: TBook; // один запис
aBooks: array[1..N] of TBook; // масив
const N = 10;
var Book: TBook; // один запис
aBooks: array[1..N] of TBook; // масив
Оголошення типу:
type TBook = record
author: string[40]; // автор, рядок
title: string[80]; // назва, рядок
year: integer;// рік видання, ціле
pages : integer; // кількість сторінок, ціле
end;
type TBook = record
author: string[40]; // автор, рядок
title: string[80]; // назва, рядок
year: integer;// рік видання, ціле
pages : integer; // кількість сторінок, ціле
end;
Пам'ять не виділяється!Пам'ять не виділяється!
!!
Оголошення змінних і масивів:
TBook – Type Book («тип книга») – зручно!
23
Записи в процедурах і функціях
Book.author := ‘О.С. Пушкін';
ShowAuthor ( Book );
Book.year := 1800;
writeln( IsOld(Book) );
Book.author := ‘О.С. Пушкін';
ShowAuthor ( Book );
Book.year := 1800;
writeln( IsOld(Book) );
Процедура:
procedure ShowAuthor ( b: TBook );
begin
writeln ( b.author );
end;
procedure ShowAuthor ( b: TBook );
begin
writeln ( b.author );
end;
Основна програма:
function IsOld( b: TBook ): boolean;
begin
IsOld := b.year < 1900;
end;
function IsOld( b: TBook ): boolean;
begin
IsOld := b.year < 1900;
end;
Функція:
24
Файли записів
Оголошення вказівника на файл:
var F: file of TBook;var F: file of TBook;
Assign(F, 'books.dat');{ зв'язати з вказівником }
Rewrite(F); { відкрити файл для запису }
writeln(F, Book); { запис }
for i:=1 to 5 do
writeln(aBook[i]); { запис }
Close(F); { закрити файл }
Assign(F, 'books.dat');{ зв'язати з вказівником }
Rewrite(F); { відкрити файл для запису }
writeln(F, Book); { запис }
for i:=1 to 5 do
writeln(aBook[i]); { запис }
Close(F); { закрити файл }
Запис у файл:
25
Читання з файлу
Відома кількість записів:
Assign(F, 'books.dat');{ зв'язати з вказівником }
Reset(F); { відкрити для читання }
Read(F, Book); { читання }
for i:=1 to 5 do
Read(F, aBook[i]); { читання }
Close(F); { закрити файл }
Assign(F, 'books.dat');{ зв'язати з вказівником }
Reset(F); { відкрити для читання }
Read(F, Book); { читання }
for i:=1 to 5 do
Read(F, aBook[i]); { читання }
Close(F); { закрити файл }
«Поки не закінчується»:
count := 0;
while not eof(F) do begin
count := count + 1; { лічильник }
Read(F, aBook[count]); { читання }
end;
count := 0;
while not eof(F) do begin
count := count + 1; { лічильник }
Read(F, aBook[count]); { читання }
end;
В чому може бути проблема!В чому може бути проблема!
??
поки не дійшли до кінця файлу F
EOF = end of file
поки не дійшли до кінця файлу F
EOF = end of file
26
Приклад програми
Задача: в файлі books.dat записані дані про книги у
вигляді масиву структур типу TBook (не більше 100).
Встановити для всіх 2008 рік видання і записати назад
в той самий файл.
type Tbook … ;
const MAX = 100;
var aBooks: array[1..MAX] of TBook;
i, N: integer;
F: file of TBook;
begin
{ прочитати записи з файлу, N - кількість }
for i:=1 to N do
aBooks[i].year := 2008;
{ зберегти у файл }
end.
type Tbook … ;
const MAX = 100;
var aBooks: array[1..MAX] of TBook;
i, N: integer;
F: file of TBook;
begin
{ прочитати записи з файлу, N - кількість }
for i:=1 to N do
aBooks[i].year := 2008;
{ зберегти у файл }
end.
type TBook … ;
повне описання
структури
повне описання
структури
27
Приклад програми
Читання «поки не закінчиться»:
Assign(f, 'books.dat');
Reset(f);
N := 0;
while not eof(F) and (N < MAX) do begin
N := N + 1;
read(F, aBooks[N]);
end;
Сlose(f);
Assign(f, 'books.dat');
Reset(f);
N := 0;
while not eof(F) and (N < MAX) do begin
N := N + 1;
read(F, aBooks[N]);
end;
Сlose(f);
Assign(f, 'books.dat'); { можна без цього }
Rewrite(f);
for i:=1 to N do write(F, aBooks[i]);
Close(f);
Assign(f, 'books.dat'); { можна без цього }
Rewrite(f);
for i:=1 to N do write(F, aBooks[i]);
Close(f);
Збереження:
що б не вийти за
межі масиву
що б не вийти за
межі масиву
28
Виділення пам'яті під запис
var pB: ^TBook;
begin
New(pB);
pB^.author := ‘О.С. Пушкін';
pB^.title := 'Полтава';
pB^.year := 1990;
pB^.pages := 129;
Dispose(pB);
end.
var pB: ^TBook;
begin
New(pB);
pB^.author := ‘О.С. Пушкін';
pB^.title := 'Полтава';
pB^.year := 1990;
pB^.pages := 129;
Dispose(pB);
end.
для звернення
до поля запису за
адресою
використовується
знак ^
для звернення
до поля запису за
адресою
використовується
знак ^
!!
New(pB);
виділення пам'яті під запис,
записати адресу в pB
виділення пам'яті під запис,
записати адресу в pB
pB^
Dispose(pB);
вивільнити
пам’ять
вивільнити
пам’ять
pB: ^TBook; змінна - вказівник
на TBook
змінна - вказівник
на TBook
29
Сортування масиву записів
Ключ (ключове поле) – це поле запису (або комбінація
полів), за яким виконується сортування.
const N = 100;
var aBooks: array[1..N] of TBook;
i, j, N: integer;
temp: TBook; { для обміну }
begin
{ заповнити масив aBooks }
{ відсортувати = переставити }
for i:=1 to N do
writeln(aBooks[i].title,
aBooks[i].year:5);
end.
const N = 100;
var aBooks: array[1..N] of TBook;
i, j, N: integer;
temp: TBook; { для обміну }
begin
{ заповнити масив aBooks }
{ відсортувати = переставити }
for i:=1 to N do
writeln(aBooks[i].title,
aBooks[i].year:5);
end.
30
Сортування масиву записів
for i:=1 to N-1 do
for j:=N-1 downto i do
if aBooks[j].year > aBooks[j+1].year
then begin
temp := aBooks[j];
aBooks[j] := aBooks[j+1];
aBooks[j+1] := temp;
end;
for i:=1 to N-1 do
for j:=N-1 downto i do
if aBooks[j].year > aBooks[j+1].year
then begin
temp := aBooks[j];
aBooks[j] := aBooks[j+1];
aBooks[j+1] := temp;
end;
Який ключ сортування?Який ключ сортування?
??
Який метод сортування?Який метод сортування?
??
Що погано?Що погано?
??
31
Сортування масиву записів
Проблема:
як уникнути копіювання запису при сортуванні?
Рішення:
використовувати допоміжний масив вказівників, при
сортуванні переставляти вказівники.
5 1 3 2 4
p[1] p[2] p[3] p[4] p[5]
p[5] p[1] p[3] p[2] p[4]
До
сортування:
Після
сортування:
Виведення результату:
for i:=1 to N do
writeln(p[i]^.title, p[i]^.year:5);
for i:=1 to N do
writeln(p[i]^.title, p[i]^.year:5);p[i]^ p[i]^
5 1 3 2 4
32
Реалізація в програмі
type PBook = ^TBook; { новий тип даних }
var p: array[1..N] of PBook;
begin
{ заповнення масиву записів}
for i:=1 to N do
p[i] := @aBooks[i];
for i:=1 to N do
writeln(p[i]^.title, p[i]^.year:5);
end.
type PBook = ^TBook; { новий тип даних }
var p: array[1..N] of PBook;
begin
{ заповнення масиву записів}
for i:=1 to N do
p[i] := @aBooks[i];
for i:=1 to N do
writeln(p[i]^.title, p[i]^.year:5);
end.
for i:=1 to N-1 do
for j:=N-1 downto i do
if p[j]^.year > p[j+1]^.year then begin
temp := p[j];
p[j] := p[j+1];
p[j+1] := temp;
end;
допоміжні
вказівники
допоміжні
вказівники
міняємо тільки
вказівники, записи
залишаються на
місцях
міняємо тільки
вказівники, записи
залишаються на
місцях
початкова
розстановка
початкова
розстановка
Тема 4. Списки
Динамічні
структури даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
34
Динамічні структури даних
Будова: набір вузлів, об'єднаних за допомогою
посилань.
Як влаштований вузол:
дані
посилання на
інші вузли
Типи структур:
списки дерева графи
nil
nilnil
однозв'язні
двонаправлені (двозв'язні)
циклічні списки (кільця)
nil
nil
nil
nil
nil nil
35
Коли потрібні списки?
Завдання (алфавітно-частотний словник). В файлі записаний
текст.
Потрібно записати в інший файл в стовпчик всі слова, що
зустрічаються в тексті, в алфавітному порядку, і кількість
повторень для кожного слова.
Проблеми:
1) кількість слів заздалегідь невідомо (статичний масив);
2) кількість слів визначається тільки в кінці роботи
(динамічний масив).
Рішення – список.
Алгоритм:
1) створити список;
2) якщо слова в файлі закінчилися, то стоп.
3) прочитати слово и шукати його у списку;
4) якщо слово знайдено – збільшити лічильник повторень,
інакше додати слово до списку;
5) перейти до кроку 2.
36
Що таке список:
1) порожня структура – це список;
2) список – це початковий вузол (голова)
і пов'язаний з ним список.
Списки: нові типи даних
type PNode = ^Node; { вказівник на вузол }
Node = record { структура вузла }
word: string[40];{ слово }
count: integer; { лічильник повторень }
next: PNode; {посилання на наступний}
end;
type PNode = ^Node; { вказівник на вузол }
Node = record { структура вузла }
word: string[40];{ слово }
count: integer; { лічильник повторень }
next: PNode; {посилання на наступний}
end;
Нові типи даних:
Адреса початку списку:
var Head: PNode;
...
Head := nil;
var Head: PNode;
...
Head := nil;
Рекурсивне
визначення!
Рекурсивне
визначення!!!
nil
Для доступу до
списку достатньо
знати адресу його
голови!
Для доступу до
списку достатньо
знати адресу його
голови!
!!
37
Що потрібно вміти робити зі списком?
1. Створити новий вузол.
2. Добавити вузол:
а) в початок списку;
б) в кінець списку;
в) після заданого вузла;
г) до заданого вузла.
3. Шукати потрібний вузол в списку.
4. Видалити вузол.
38
Створення вузла
function CreateNode(NewWord: string): PNode;
var NewNode: PNode;
begin
New(NewNode);
NewNode^.word := NewWord;
NewNode^.count := 1;
NewNode^.next := nil;
Result := NewNode;
end;
function CreateNode(NewWord: string): PNode;
var NewNode: PNode;
begin
New(NewNode);
NewNode^.word := NewWord;
NewNode^.count := 1;
NewNode^.next := nil;
Result := NewNode;
end;
Функція CreateNode (створити вузол):
вхід: нове слово, прочитане з файлу;
вихід: адреса нового вузла, створеного в пам'яті.
повертає адресу
створеного вузла
повертає адресу
створеного вузланове словонове слово
Якщо пам'ять
виділити не
вдалося?
Якщо пам'ять
виділити не
вдалося?
??
39
Добавлення вузла в початок списку
NewNodeNewNode
HeadHead nil
1) Встановити посилання нового вузла на голову списку:
NewNode^.next := Head;NewNode^.next := Head;NewNodeNewNode
HeadHead nil
nil
2) Встановити новий вузол як голову списку:
Head := NewNode;Head := NewNode;
procedure AddFirst ( var Head: PNode; NewNode: PNode );
begin
NewNode^.next := Head;
Head := NewNode;
end;
procedure AddFirst ( var Head: PNode; NewNode: PNode );
begin
NewNode^.next := Head;
Head := NewNode;
end;
var
адреса голови міняєтьсяадреса голови міняється
40
Добавлення вузла після заданого
1) Встановити посилання нового вузла на вузол, наступний за p:
NewNode^.next = p^.next;NewNode^.next = p^.next;
2) Встановити посилання вузла p на новий вузол:
p^.next = NewNode;p^.next = NewNode;
NewNodeNewNode
pp
nil
nil
NewNodeNewNode
pp
nil
procedure AddAfter ( p, NewNode: PNode );
begin
NewNode^.next := p^.next;
p^.next := NewNode;
end;
procedure AddAfter ( p, NewNode: PNode );
begin
NewNode^.next := p^.next;
p^.next := NewNode;
end;
41
Завдання:
зробити що-небудь хороше з кожним елементом списку.
Алгоритм:
1) встановити допоміжний вказівник q на голову списку;
2) якщо вказівник q дорівнює nil (дійшли до кінця списку), то
стоп;
3) виконати дію над вузлом з адресою q ;
4) перейти до наступного вузла, q^.next.
Прохід по списку
var q: PNode;
...
q := Head; // почали з голови
while q <> nil do begin // поки не дійшли до кінця
... // робимо щось хороше з q
q := q^.next; // переходимо до наступного
end;
var q: PNode;
...
q := Head; // почали з голови
while q <> nil do begin // поки не дійшли до кінця
... // робимо щось хороше з q
q := q^.next; // переходимо до наступного
end;
HeadHead nil
qq
42
Добавлення вузла в кінець списку
Завдання: додати новий вузол в кінець списку.
Алгоритм:
1) знайти останній вузол q, такий що q^.next дорівнює nil;
2) добавити вузол після вузла з адресою q (процедура AddAfter).
Особливий випадок: добавлення в порожній список.
procedure AddLast ( var Head: PNode; NewNode: PNode );
var q: PNode;
begin
if Head = nil then
AddFirst ( Head, NewNode )
else begin
q := Head;
while q^.next <> nil do
q := q^.next;
AddAfter ( q, NewNode );
end;
end;
procedure AddLast ( var Head: PNode; NewNode: PNode );
var q: PNode;
begin
if Head = nil then
AddFirst ( Head, NewNode )
else begin
q := Head;
while q^.next <> nil do
q := q^.next;
AddAfter ( q, NewNode );
end;
end;
особливий випадок –
добавлення в порожній список
особливий випадок –
добавлення в порожній список
шукаємо останній вузолшукаємо останній вузол
добавити вузол
після вузла q
добавити вузол
після вузла q
43
Проблема:
потрібно знати адресу попереднього вузла, а йти назад не можна!
Рішення: знайти попередній вузол q (прохід з початку списку).
Добавлення вузла перед заданим
NewNodeNewNode
pp
nil
nil
procedure AddBefore(var Head: PNode; p, NewNode: PNode);
var q: PNode;
begin
q := Head;
if p = Head then
AddFirst ( Head, NewNode )
else begin
while (q <> nil) and (q^.next <> p) do
q := q^.next;
if q <> nil then AddAfter ( q, NewNode );
end;
end;
procedure AddBefore(var Head: PNode; p, NewNode: PNode);
var q: PNode;
begin
q := Head;
if p = Head then
AddFirst ( Head, NewNode )
else begin
while (q <> nil) and (q^.next <> p) do
q := q^.next;
if q <> nil then AddAfter ( q, NewNode );
end;
end;
в початок спискув початок списку
шукаємо вузол, наступний
за яким - вузол p
шукаємо вузол, наступний
за яким - вузол p
добавити вузол
після вузла q
добавити вузол
після вузла q
Що погано?Що погано???
44
Добавлення вузла перед заданим (II)
Завдання: вставити вузол перед заданим без пошуку попереднього.
Алгоритм:
1) поміняти місцями дані нового вузла і вузла p;
2) встановити посилання вузла p на NewNode.
procedure AddBefore2 ( p, NewNode: PNode );
var temp: Node;
begin
temp := p^; p^ := NewNode^;
NewNode^ := temp;
p^.next := NewNode;
end;
procedure AddBefore2 ( p, NewNode: PNode );
var temp: Node;
begin
temp := p^; p^ := NewNode^;
NewNode^ := temp;
p^.next := NewNode;
end;
NewNodeNewNode
pp
nil
Так не можна, якщо
p = nil або адреси
вузлів десь
ще запам'ятовуються!
Так не можна, якщо
p = nil або адреси
вузлів десь
ще запам'ятовуються!
!!
NewNodeNewNode
pp
nil
nil
45
Пошук слова в списку
Завдання:
знайти в списку задане слово або визначити, що його немає.
Функція Find:
вхід: слово (символьний рядок);
вихід: адреса вузла, з цим словом або nil.
Алгоритм: прохід по списку.
function Find(Head: PNode; NewWord: string): PNode;
var q: PNode;
begin
q := Head;
while (q <> nil) and (NewWord <> q^.word) do
q := q^.next;
Result := q;
end;
function Find(Head: PNode; NewWord: string): PNode;
var q: PNode;
begin
q := Head;
while (q <> nil) and (NewWord <> q^.word) do
q := q^.next;
Result := q;
end;
шукаємо це словошукаємо це слово
результат – адреса
вузла або nil (нема
такого)
результат – адреса
вузла або nil (нема
такого)
while (q <> nil) and (NewWord <> q^.word) do
q := q^.next;
поки не дійшли до кінця списку
і слово не дорівнює заданому
поки не дійшли до кінця списку
і слово не дорівнює заданому
46
Куди вставити нове слово?
Завдання:
знайти вузол, перед яким потрібно вставити, задане слово, так щоб
у списку зберігався алфавітний порядок слів.
Функція FindPlace:
вхід: слово (символьний рядок);
вихід: адреса вузла, перед яким потрібно вставити це слово або
nil, якщо слово потрібно вставити в кінець списку.
function FindPlace(Head: PNode; NewWord: string): PNode;
var q: PNode;
begin
q := Head;
while (q <> nil) and (NewWord > q^.word) do
q := q^.next;
Result := q;
end;
function FindPlace(Head: PNode; NewWord: string): PNode;
var q: PNode;
begin
q := Head;
while (q <> nil) and (NewWord > q^.word) do
q := q^.next;
Result := q;
end;
>
слово NewWord стоїть за
алфавітом перед q^.word
слово NewWord стоїть за
алфавітом перед q^.word
47
Видалення вузла
procedure DeleteNode ( var Head: PNode; p: PNode );
var q: PNode;
begin
if Head = p then
Head := p^.next
else begin
q := Head;
while (q <> nil) and (q^.next <> p) do
q := q^.next;
if q <> nil then q^.next := p^.next;
end;
Dispose(p);
end;
procedure DeleteNode ( var Head: PNode; p: PNode );
var q: PNode;
begin
if Head = p then
Head := p^.next
else begin
q := Head;
while (q <> nil) and (q^.next <> p) do
q := q^.next;
if q <> nil then q^.next := p^.next;
end;
Dispose(p);
end;
while (q <> nil) and (q^.next <> p) do
q := q^.next;
if Head = p then
Head := p^.next
qq
HeadHead
pp
nil
Проблема: потрібно знати адресу попереднього вузла q.
особливий випадок:
видаляємо перший вузол
особливий випадок:
видаляємо перший вузол
шукаємо вузол, такий що
q^.next = p
шукаємо вузол, такий що
q^.next = p
Dispose(p);
звільнення пам'ятізвільнення пам'яті
48
Алфавітно-частотний словник
Алгоритм:
1) відкрити файл для читання;
2) прочитати чергове слово (як?)
3) якщо файл закінчився, то перейти до кроку 7;
4) якщо слово знайдено, збільшити лічильник (поле count);
5) якщо слова немає в списку, то
• створити новий вузол, заповнити поля (CreateNode);
• знайти вузол, перед яким потрібно вставити слово
(FindPlace);
• добавити вузол (AddBefore);
1) перейти до кроку 2;
2) закрити файл
3) вивести список слів, використовуючи прохід по списку.
var F: Text;
...
Assign(F, 'input.dat');
Reset ( F );
var F: Text;
...
Assign(F, 'input.dat');
Reset ( F );
Close ( F );Close ( F );
49
Як прочитати одне слово з файлу?
function GetWord ( F: Text ) : string;
var c: char;
begin
Result := ''; { порожній рядок }
c := ' '; { пропуск – щоб увійти в цикл }
{ пропускаємо спецсимволи і пропуски }
while not eof(f) and (c <= ' ') do
read(F, c);
{ читаєм слово }
while not eof(f) and (c > ' ') do begin
Result := Result + c;
read(F, c);
end;
end;
function GetWord ( F: Text ) : string;
var c: char;
begin
Result := ''; { порожній рядок }
c := ' '; { пропуск – щоб увійти в цикл }
{ пропускаємо спецсимволи і пропуски }
while not eof(f) and (c <= ' ') do
read(F, c);
{ читаєм слово }
while not eof(f) and (c > ' ') do begin
Result := Result + c;
read(F, c);
end;
end;
Алгоритм:
1) пропускаємо всі спецсимволи і пропуски (з кодами <= 32)
2) читаємо всі символи до першого пропуску або спецсимволу
Можна поміняти місцями
рядки в циклі?
Можна поміняти місцями
рядки в циклі?
??
50
Повна програма
type PNode = ^Node;
Node = record ... end; { нові типи даних }
var Head: PNode; { адреса голови списку }
NewNode, q: PNode; { допоміжні вказівники }
w: string; { слово з файлу }
F: text; { файлова змінна }
count: integer; { лічильник різних слів }
{ процедури і функції }
begin
Head := nil;
Assign ( F, 'input.txt' );
Reset ( F );
{ читаємо слова з файлу, будуємо список }
Close ( F );
{ виводимо список в інший файл }
end.
type PNode = ^Node;
Node = record ... end; { нові типи даних }
var Head: PNode; { адреса голови списку }
NewNode, q: PNode; { допоміжні вказівники }
w: string; { слово з файлу }
F: text; { файлова змінна }
count: integer; { лічильник різних слів }
{ процедури і функції }
begin
Head := nil;
Assign ( F, 'input.txt' );
Reset ( F );
{ читаємо слова з файлу, будуємо список }
Close ( F );
{ виводимо список в інший файл }
end.
51
Повна програма (II)
{ читаємо слова з файлу, будуємо список }
while True do begin { нескінченний цикл }
w := GetWord ( F ); { читаємо слово }
if w = '' then break; { слова закінчились, вихід }
q := Find ( Head, w ); { шукаємо слово в списку }
if q <> nil then { знайшли, збільшити лічильник
}
q^.count := q^.count + 1
else begin { не знайшли, добавити в список
}
NewNode := CreateNode ( w );
q := FindPlace ( Head, w );
AddBefore ( Head, q, NewNode );
end;
end;
{ читаємо слова з файлу, будуємо список }
while True do begin { нескінченний цикл }
w := GetWord ( F ); { читаємо слово }
if w = '' then break; { слова закінчились, вихід }
q := Find ( Head, w ); { шукаємо слово в списку }
if q <> nil then { знайшли, збільшити лічильник
}
q^.count := q^.count + 1
else begin { не знайшли, добавити в список
}
NewNode := CreateNode ( w );
q := FindPlace ( Head, w );
AddBefore ( Head, q, NewNode );
end;
end;
52
Повна програма (III)
{ виводимо список в інший файл }
q := Head; { прохід з початку списку }
count := 0; { обнулили лічильник слів }
Assign(F, 'output.txt');
Rewrite(F);
while q <> nil do begin { поки не кінець
списку }
count := count + 1; { ще одне слово }
writeln ( F, q^.word, ': ', q^.count );
q := q^.next; { перейти до
наступного }
end;
writeln ( F, ‘Знайдено ',
{ виводимо список в інший файл }
q := Head; { прохід з початку списку }
count := 0; { обнулили лічильник слів }
Assign(F, 'output.txt');
Rewrite(F);
while q <> nil do begin { поки не кінець
списку }
count := count + 1; { ще одне слово }
writeln ( F, q^.word, ': ', q^.count );
q := q^.next; { перейти до
наступного }
end;
writeln ( F, ‘Знайдено ',
type PNode = ^Node; { вказівник на вузол }
Node = record { структура вузла }
word: string[40]; { слово }
count: integer; {лічильник повторений}
next: PNode; {посилання на наступний}
prev: PNode; {посилання на попередній}
end;
type PNode = ^Node; { вказівник на вузол }
Node = record { структура вузла }
word: string[40]; { слово }
count: integer; {лічильник повторений}
next: PNode; {посилання на наступний}
prev: PNode; {посилання на попередній}
end;
53
Двозв’язні списки
Структура вузла:
Адреси «голови» і «хвоста»:
var Head, Tail: PNode;
...
Head := nil; Tail := nil;
var Head, Tail: PNode;
...
Head := nil; Tail := nil;
nextprev
previous
можна рухатися в
обидва боки
потрібно працювати з
двома вказівниками
замість одного
nilnil
HeadHead TailTail
54
Завдання
«4»: «Зібрати» з цих функцій програму для побудови
алфавітно-частотного словника. В кінці файлу
вивести загальну кількість різних слів (кількість
елементів списку).
«5»: Те ж саме, але використовувати двозв’язні
списки.
«6»: Те ж саме, що і на «5», але вивести список слів в
порядку зменшення частоти, тобто, спочатку ті
слова, які зустрічаються найчастіше.
Тема 5. Стеки, черги, деки
Динамічні
структури даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
56
Стек
Стек – це лінійна структура даних, в якій додавання і видалення
елементів можливо тільки з одного кінця (вершини стека). Stack =
кипа, куча, стопка (англ.)
LIFO = Last In – First Out
«Хто останнім увійшов, той першим вийшов».
Операції зі стеком:
1) додати елемент на вершину
(Push = заштовхнути);
2) зняти елемент з вершини
(Pop = вилетіти зі звуком).
57
Приклад завдання
Завдання: вводиться символьний рядок, в якому записано вираз з
дужками трьох типів: [], {} і (). Визначити, чи правильно
розставлені дужки (не звертаючи уваги на інші символи).
Приклади:
[()]{} ][ [({)]}
Спрощене завдання: те ж саме, але з одним видом дужок.
Рішення: лічильник вкладеності дужок. Послідовність правильна,
якщо в кінці лічильник дорівнює нулю і при проході ні разу не
ставав негативним.
Чи можна вирішити
вихідне завдання так
само, але з трьома
лічильниками?
Чи можна вирішити
вихідне завдання так
само, але з трьома
лічильниками?
?? [ ( { ) ] }
(: 0 1 0
[: 0 1 0
{: 0 1 0
[ ( { ) ] }[ ( { ) ] }
( ( ) ) ( )
1 2 1 0 1 0
( ( ) ) ( )( ( ) ) ( ) ( ( ) ) ) (
1 2 1 0 -1 0
( ( ) ) ) (( ( ) ) ) ( ( ( ) ) (
1 2 1 0 1
( ( ) ) (( ( ) ) (
58
Рішення завдання з дужками
Алгоритм:
1) на початку стек порожній;
2) в циклі переглядаємо всі символи рядка по порядку;
3) якщо черговий символ – відкриваюча дужка, заносимо її на
вершину стека;
4) якщо символ – закриваюча дужка, перевіряємо вершину стека:
там повинна бути відповідна відкриваюча дужка (якщо це не
так, то помилка);
5) якщо наприкінці стек не порожній, вираз неправильний.
[ ( ( ) ) ] { }[ ( ( ) ) ] { }
[ [
(
[
(
(
[
(
(
[
(
[ { {
59
Реалізація стека (масив)
Структура-стек:
const MAXSIZE = 100;
type Stack = record { стек на 100 символів }
data: array[1..MAXSIZE] of char;
size: integer; { кількість елементів }
end;
const MAXSIZE = 100;
type Stack = record { стек на 100 символів }
data: array[1..MAXSIZE] of char;
size: integer; { кількість елементів }
end;
Додавання елемента:
procedure Push( var S: Stack; x: char);
begin
if S.size = MAXSIZE then Exit;
S.size := S.size + 1;
S.data[S.size] := x;
end;
procedure Push( var S: Stack; x: char);
begin
if S.size = MAXSIZE then Exit;
S.size := S.size + 1;
S.data[S.size] := x;
end;
помилка:
переповнення
стека
помилка:
переповнення
стека
добавити елементдобавити елемент
Що погано?Що погано?
??
60
Реалізація стека (масив)
function Pop ( var S:Stack ): char;
begin
if S.size = 0 then begin
Result := char(255);
Exit;
end;
Result := S.data[S.size];
S.size := S.size - 1;
end;
function Pop ( var S:Stack ): char;
begin
if S.size = 0 then begin
Result := char(255);
Exit;
end;
Result := S.data[S.size];
S.size := S.size - 1;
end;
Зняття елемента з вершини:
Порожній чи ні?
function isEmpty ( S: Stack ): Boolean;
begin
Result := (S.size = 0);
end;
function isEmpty ( S: Stack ): Boolean;
begin
Result := (S.size = 0);
end;
помилка:
стек порожній
помилка:
стек порожній
61
Програма
var br1, br2, expr: string;
i, k: integer;
upper: char; { то, що зняли зі стека }
error: Boolean; { ознака помилки }
S: Stack;
begin
br1 := '([{'; br2 := ')]}';
S.size := 0;
error := False;
writeln('Введіть вираз з дужками');
readln(expr);
... { тут буде основний цикл обробки }
if not error and isEmpty(S) then
writeln('Вираз правильний.')
else writeln('Вираз неправильний.')
end.
var br1, br2, expr: string;
i, k: integer;
upper: char; { то, що зняли зі стека }
error: Boolean; { ознака помилки }
S: Stack;
begin
br1 := '([{'; br2 := ')]}';
S.size := 0;
error := False;
writeln('Введіть вираз з дужками');
readln(expr);
... { тут буде основний цикл обробки }
if not error and isEmpty(S) then
writeln('Вираз правильний.')
else writeln('Вираз неправильний.')
end.
відкриваючі дужкивідкриваючі дужки
закриваючі дужкизакриваючі дужки
62
Обробка рядка (основний цикл)
for i:=1 to length(expr)
do begin
for k:=1 to 3 do begin
if expr[i] = br1[k] then begin { відкр. дужка }
Push(S, expr[i]); { заштовхнути в стек}
break;
end;
if expr[i] = br2[k] then begin { закр. дужка }
upper := Pop(S); { зняти символ із стека }
error := upper <> br1[k];
break;
end;
end;
if error then break;
end;
for i:=1 to length(expr)
do begin
for k:=1 to 3 do begin
if expr[i] = br1[k] then begin { відкр. дужка }
Push(S, expr[i]); { заштовхнути в стек}
break;
end;
if expr[i] = br2[k] then begin { закр. дужка }
upper := Pop(S); { зняти символ із стека }
error := upper <> br1[k];
break;
end;
end;
if error then break;
end;
цикл по всіх символах
рядка expr
цикл по всіх символах
рядка expr
цикл за всіма видами дужокцикл за всіма видами дужок
помилка: стек
порожній або не
та дужка
помилка: стек
порожній або не
та дужка
була помилка: далі
немає сенсу перевіряти
була помилка: далі
немає сенсу перевіряти
63
Реалізація стека (список)
Додавання елемента:
Структура вузла:
type PNode = ^Node; { вказівник на вузол }
Node = record
data: char; { дані }
next: PNode; { вказівник на наст. елемент }
end;
type PNode = ^Node; { вказівник на вузол }
Node = record
data: char; { дані }
next: PNode; { вказівник на наст. елемент }
end;
procedure Push( var Head: PNode; x: char);
var NewNode: PNode;
begin
New(NewNode); { виділити пам’ять }
NewNode^.data := x; { записати символ }
NewNode^.next := Head; { зробити першим вузлом }
Head := NewNode;
end;
procedure Push( var Head: PNode; x: char);
var NewNode: PNode;
begin
New(NewNode); { виділити пам’ять }
NewNode^.data := x; { записати символ }
NewNode^.next := Head; { зробити першим вузлом }
Head := NewNode;
end;
64
Реалізація стека (список)
Зняття елемента з вершини:
function Pop ( var Head: PNode ): char;
var q: PNode;
begin
if Head = nil then begin { стек порожній }
Result := char(255);{невикористовуваний символ}
Exit;
end;
Result := Head^.data; { взяти верхній символ }
q := Head; { запам’ятати вершину }
Head := Head^.next; { видалити вершину з стека }
Dispose(q); { видалити з пам’яті }
end;
function Pop ( var Head: PNode ): char;
var q: PNode;
begin
if Head = nil then begin { стек порожній }
Result := char(255);{невикористовуваний символ}
Exit;
end;
Result := Head^.data; { взяти верхній символ }
q := Head; { запам’ятати вершину }
Head := Head^.next; { видалити вершину з стека }
Dispose(q); { видалити з пам’яті }
end;
Чи можна переставляти оператори?Чи можна переставляти оператори?
??
q := Head;
Head := Head^.next;
Dispose(q);
q := Head;
Head := Head^.next;
Dispose(q);
65
Реалізація стека (список)
Зміни в основній програмі:
var S: Stack;
...
S.size := 0;
var S: Stack;
...
S.size := 0;
var S: PNode;var S: PNode;
S := nil;S := nil;
Більше нічого не змінюється!Більше нічого не змінюється!
!!
Порожній чи ні?
function isEmpty ( S: Stack ): Boolean;
begin
Result := (S = nil);
end;
function isEmpty ( S: Stack ): Boolean;
begin
Result := (S = nil);
end;
66
Обчислення арифметичних виразів
a b + c d + 1 - /a b + c d + 1 - /
Як обчислювати автоматично:
Інфіксний запис
(знак операції між операндами)
(a + b) / (c + d – 1)(a + b) / (c + d – 1)
необхідні дужки!
Постфіксний запис (знак операції після операндів)
польська нотація,
Jan Łukasiewicz (1920)
польська нотація,
Jan Łukasiewicz (1920)
дужки не потрібні, можна однозначно
обчислити!
Префіксний запис (знак операції до операндів)
/ + a b - + c d 1/ + a b - + c d 1
зворотна польська нотація,
F. L. Bauer and E. W. Dijkstra
зворотна польська нотація,
F. L. Bauer and E. W. Dijkstra
a + ba + b
a + ba + b
c + dc + d
c + dc + d
c + d - 1c + d - 1
c + d - 1c + d - 1
67
Запишіть в постфіксній формі
(32*6-5)*(2*3+4)/(3+7*2)(32*6-5)*(2*3+4)/(3+7*2)
(2*4+3*5)*(2*3+18/3*2)*(12-3)(2*4+3*5)*(2*3+18/3*2)*(12-3)
(4-2*3)*(3-12/3/4)*(24-3*12)(4-2*3)*(3-12/3/4)*(24-3*12)
68
Обчислення виразів
Постфіксна форма:
a b + c d + 1 - /a b + c d + 1 - /
Алгоритм:
1) взяти черговий елемент;
2) якщо це не знак операції, добавить його в стек;
3) якщо це знак операції, то
• взяти з стека два операнди;
• виконати операцію і записати результат в стек;
1) перейти до кроку 1.
a
b
a a+b
c
a+b
d
c
a+b
c+d
a+b
1
c+d
a+b
c+d-1
a+b X
X =X =
69
Системний стек (Windows – 1 Мб)
Використовується для
1) розміщення локальних змінних;
2) зберігання адрес повернення (за якими переходить
програма після виконання функції або процедури);
3) передачі параметрів в функції та процедури;
4) тимчасового зберігання даних (в програмах на мові
Асемблер).
Переповнення стека (stack overflow):
1) занадто багато локальних змінних
(вихід – використовувати динамічні масиви);
2) дуже багато рекурсивних викликів функцій і процедур
(вихід – переробити алгоритм так, щоб зменшити глибину
рекурсії або відмовитися від неї взагалі).
70
Черга
1
2
3
4
5
6
Черга – це лінійна структура даних, в якій
додавання елементів можливе тільки з одного кінця
(кінця черги), а видалення елементів – тільки з іншого кінця
(початку черги).
FIFO = First In – First Out
«Хто першим увійшов, той першим вийшов».
Операції з чергою:
1) додати елемент в кінець черги (PushTail = заштовхнути
в кінець);
2) видалити елемент з початку черги (Pop).
71
Реалізація черги (масив)
1
1 2
1 2 3
1 2 3
найпростіший спосіб
1) потрібно заздалегідь виділити масив;
2) при вибірці з черги потрібно зрушувати всі елементи.
72
Реалізація черги (кільцевий масив)
1 2
HeadHead TailTail
1 2 3
2 3 2 3 4
3 4 Скільки елементів
можна зберігати в
такій черзі?
Скільки елементів
можна зберігати в
такій черзі?
??
Як розрізнити
стани «черга
порожня» і «черга
повна»?
Як розрізнити
стани «черга
порожня» і «черга
повна»?
??3 45
73
Реалізація черги (кільцевий масив)
1
HeadHead TailTail
В черзі 1 елемент:
Черга порожня:
Черга повна:
Head = Tail + 1Head = Tail + 1 Head = (Tail mod N) + 1Head = (Tail mod N) + 1
1 N
розмір
масиву
розмір
масиву
1 23
Head = Tail + 2Head = Tail + 2 Head = (Tail+1) mod N + 1Head = (Tail+1) mod N + 1
1 N
1 2 3
Head = TailHead = Tail
74
Реалізація черги (кільцевий масив)
type Queue = record
data: array[1..MAXSIZE] of integer;
head, tail: integer;
end;
type Queue = record
data: array[1..MAXSIZE] of integer;
head, tail: integer;
end;
Структура даних:
Додавання в чергу:
procedure PushTail( var Q: Queue; x: integer);
begin
if Q.head = (Q.tail+1) mod MAXSIZE + 1
then Exit; { черга повна, не додавати }
Q.tail := Q.tail mod MAXSIZE + 1;
Q.data[Q.tail] := x;
end;
procedure PushTail( var Q: Queue; x: integer);
begin
if Q.head = (Q.tail+1) mod MAXSIZE + 1
then Exit; { черга повна, не додавати }
Q.tail := Q.tail mod MAXSIZE + 1;
Q.data[Q.tail] := x;
end;
замкнути
в кільце
замкнути
в кільцеmod MAXSIZE
75
Реалізація черги (кільцевий масив)
Вибірка з черги:
function Pop ( var S: Queue ): integer;
begin
if Q.head = Q.tail mod MAXSIZE + 1 then begin
Result := MaxInt;
Exit;
end;
Result := Q.data[Q.head];
Q.head := Q.head mod MAXSIZE + 1;
end;
function Pop ( var S: Queue ): integer;
begin
if Q.head = Q.tail mod MAXSIZE + 1 then begin
Result := MaxInt;
Exit;
end;
Result := Q.data[Q.head];
Q.head := Q.head mod MAXSIZE + 1;
end;
черга
порожня
черга
порожня
взяти перший
елемент
взяти перший
елемент
видалити його
з черги
видалити його
з черги
максимальне
ціле число
максимальне
ціле число
76
Реалізація черги (списки)
type PNode = ^Node;
Node = record
data: integer;
next: PNode;
end;
type PNode = ^Node;
Node = record
data: integer;
next: PNode;
end;
type Queue = record
head, tail: PNode;
end;
type Queue = record
head, tail: PNode;
end;
Структура вузла:
Тип даних «черга»:
77
Реалізація черги (списки)
procedure PushTail( var Q: Queue; x: integer );
var NewNode: PNode;
begin
New(NewNode);
NewNode^.data := x;
NewNode^.next := nil;
if Q.tail <> nil then
Q.tail^.next := NewNode;
Q.tail := NewNode; { новий вузол – в кінець}
if Q.head = nil then Q.head := Q.tail;
end;
procedure PushTail( var Q: Queue; x: integer );
var NewNode: PNode;
begin
New(NewNode);
NewNode^.data := x;
NewNode^.next := nil;
if Q.tail <> nil then
Q.tail^.next := NewNode;
Q.tail := NewNode; { новий вузол – в кінець}
if Q.head = nil then Q.head := Q.tail;
end;
Додавання елемента:
створюємо
новий вузол
створюємо
новий вузол
якщо в списку вже
щось було,
додаємо в кінець
якщо в списку вже
щось було,
додаємо в кінець
якщо в списку
нічого не було, …
якщо в списку
нічого не було, …
78
Реалізація черги (списки)
function Pop ( var S: Queue ): integer;
var top: PNode;
begin
if Q.head = nil then begin
Result := MaxInt;
Exit;
end;
top := Q.head;
Result := top^.data;
Q.head := top^.next;
if Q.head = nil then Q.tail := nil;
Dispose(top);
end;
function Pop ( var S: Queue ): integer;
var top: PNode;
begin
if Q.head = nil then begin
Result := MaxInt;
Exit;
end;
top := Q.head;
Result := top^.data;
Q.head := top^.next;
if Q.head = nil then Q.tail := nil;
Dispose(top);
end;
Вибірка елемента:
якщо список
порожній, …
якщо список
порожній, …
запам'ятали
перший елемент
запам'ятали
перший елемент
якщо в списку
нічого не
залишилося, …
якщо в списку
нічого не
залишилося, …
звільнити
пам'ять
звільнити
пам'ять
79
Дека
Дека (deque = double ended queue, черга з двома
кінцями)– це лінійна структура даних, в якій додавання
і видалення елементів можливе з обох кінців.
12 34 56
Операції з декою:
1) додавання елемента в початок (Push);
2) видалення елемента з початку (Pop);
3) додавання елемента в кінець (PushTail);
4) видалення елемента з кінця (PopTail).
Реалізація:
1) кільцевий масив;
2) двохзв'язний список.
80
Завдання
«4»: У файлі input.dat знаходиться список чисел
(або слів). Переписати його в файл
output.dat в зворотному порядку.
«5»: Скласти програму, яка обчислює значення
арифметичного виразу, записаного в
постфіксній формі, за допомогою стека. Вираз
правильний, допускаються тільки однозначні
числа і знаки +, -, *, /.
«6»: Те ж саме, що і в «5», але допускаються
багатозначні числа.
Тема 6. Дерева
Динамічні
структури даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
82
Дерева
директордиректор
гол. інженергол. інженер гол. бухгалтергол. бухгалтер
інженерінженер
інженерінженер
інженерінженер
бухгалтербухгалтер
бухгалтербухгалтер
бухгалтербухгалтер
Що спільного в усіх
прикладах?
Що спільного в усіх
прикладах?
??
83
Дерева
Дерево – це структура даних, що
складається з вузлів і з'єднують їх
спрямовані ребра (дуги), причому в кожен
вузол (крім кореневого) входить тільки
одна дуга.
Корінь – це початковий вузол дерева.
Листок – це вузол, з якого не виходить
жодної дуги.
корінькорінь
22
88
55
66
99
11
33
44
1010
77
Які структури – не дерева?
44
11
3322
55
22
44
66
11
33
55
33
22
11
3322
11
44
84
Дерева
Предок вузла x – це вузол, з якого існує шлях по
стрілках у вузол x.
Потомок вузла x – це вузол, в який існує шлях по
стрілках з вузла x.
Батько вузла x – це вузол, з якого існує дуга
безпосередньо у вузол x.
За допомогою дерев зображають відносини
підпорядкованості (ієрархія, «старший – молодший»,
«батько – дитина»).
За допомогою дерев зображають відносини
підпорядкованості (ієрархія, «старший – молодший»,
«батько – дитина»).
!!
22
44
66
11
33
55
Син вузла x – це вузол, в який існує дуга безпосередньо з вузла x.
Брат вузла x (sibling) – це вузол, у якого той же батько, що й у
вузла x.
Висота дерева – це найбільша відстань від кореня до листка
(кількість дуг).
85
Дерево – рекурсивна структура даних
Рекурсивне визначення:
1. Порожня структура – це дерево.
2. Дерево – це корінь і кілька
пов'язаних з ним дерев.
22
44
66
11
33
55
Двійкове (бінарне) дерево – це дерево, в
якому кожен вузол має не більше двох
синів.
1. Порожня структура – це двійкове дерево.
2. Двійкове дерево – це корінь і два пов'язаних з
ним двійкових дерева (ліве і праве піддерева).
86
Двійкові дерева
Структура вузла:
type PNode = ^Node; { покажчик на вузол }
Node = record
data: integer; { корисні дані }
left, right: PNode; { посилання на лівого і
правого синів }
end;
type PNode = ^Node; { покажчик на вузол }
Node = record
data: integer; { корисні дані }
left, right: PNode; { посилання на лівого і
правого синів }
end;
Застосування:
1) пошук даних у спеціально побудованих деревах
(бази даних);
2) сортування даних;
3) обчислення арифметичних виразів;
4) кодування (метод Хаффмана).
87
Двійкові дерева пошуку
1616 4545
3030
7676 125125
9898
5959
Яка закономірність?Яка закономірність?
??
Зліва від кожного вузла знаходяться
вузли з меншими ключами, а справа –
з більшими.
Ключ – це характеристика вузла, по якій виконується
пошук (найчастіше – одне з полів структури).
Як шукати ключ, рівний x:
1) якщо дерево порожнє, ключ не знайдений;
2) якщо ключ вузла дорівнює x, то стоп.
3) якщо ключ вузла менше x, то шукати x в лівому піддереві;
4) якщо ключ вузла більше x, то шукати x в правом піддереві.
Зведення задачі до такої ж задачі меншою
розмірності – це …?
Зведення задачі до такої ж задачі меншою
розмірності – це …?
??
88
Двійкові дерева пошуку
1616 4545
3030
7676 125125
9898
5959
Пошук в масиві (N елементів):
1616454530307676 12512598985959
При кожному порівнянні відкидається 1 елемент.
Кількість порівнянь – N.
Пошук по дереву (N елементів):
При кожному порівнянні
відкидається половина
залишившихся елементів.
Кількість порівнянь ~ log2N.
швидкий пошук
1) потрібно заздалегідь побудувати дерево;
2) бажано, щоб дерево було мінімальної висоти.
89
Реалізація алгоритму пошуку
{ Функція Search – пошук по дереву
Вхід: Tree - адреса кореня,
x - що шукаємо
Вихід: адреса вузла або nil (не знайшли) }
function Search(Tree: PNode; x: integer): PNode;
begin
if Tree = nil then begin
Result := nil;
Exit;
end;
if x = Tree^.data then
Result := Tree
else
if x < Tree^.data then
Result := Search(Tree^.left, x)
else Result := Search(Tree^.right, x);
end;
{ Функція Search – пошук по дереву
Вхід: Tree - адреса кореня,
x - що шукаємо
Вихід: адреса вузла або nil (не знайшли) }
function Search(Tree: PNode; x: integer): PNode;
begin
if Tree = nil then begin
Result := nil;
Exit;
end;
if x = Tree^.data then
Result := Tree
else
if x < Tree^.data then
Result := Search(Tree^.left, x)
else Result := Search(Tree^.right, x);
end;
дерево порожнє:
ключ не знайшли…
дерево порожнє:
ключ не знайшли…
знайшли,
повертаємо
адресу кореня
знайшли,
повертаємо
адресу кореня шукати в
лівому
піддереві
шукати в
лівому
піддереві
шукати в правому піддеревішукати в правому піддереві
90
Як побудувати дерево пошуку?
{ Процедура AddToTree – додати елемент
Вхід: Tree - адреса кореня,
x - що додаємо }
procedure AddToTree( var Tree: PNode; x: integer );
begin
if Tree = nil then begin
New(Tree);
Tree^.data := x;
Tree^.left := nil;
Tree^.right := nil;
Exit;
end;
if x < Tree^.data then
AddToTree(Tree^.left, x)
else AddToTree(Tree^.right, x);
end;
{ Процедура AddToTree – додати елемент
Вхід: Tree - адреса кореня,
x - що додаємо }
procedure AddToTree( var Tree: PNode; x: integer );
begin
if Tree = nil then begin
New(Tree);
Tree^.data := x;
Tree^.left := nil;
Tree^.right := nil;
Exit;
end;
if x < Tree^.data then
AddToTree(Tree^.left, x)
else AddToTree(Tree^.right, x);
end;
дерево порожнє: створюємо
новий вузол (корінь)
дерево порожнє: створюємо
новий вузол (корінь)
адреса кореня
може змінюватися
адреса кореня
може змінюватися
додаємо до лівого
або правого
піддерево
додаємо до лівого
або правого
піддерево
Мінімальна висота не гарантується!Мінімальна висота не гарантується!
!!
91
Обхід дерева
1616 4545
3030
7676 125125
9898
5959Обхід дерева – це перерахування
всіх вузлів в певному порядку.
Обхід ЛКП («лівий – корінь –
правий»):
125125989876764545 595930301616
Обхід ПКЛ («правий – корінь – лівий»):
1616303045457676 59599898125125
Обхід КЛП («корінь – лівий – правий»):
125125767698981616 454530305959
Обхід ЛПК («лівий – правий – корінь»):
595998981251253030 767645451616
92
Обхід дерева – реалізація
{ Процедура LKP – обхід дерева в порядку ЛКП
(лівий – корінь – правий)
Вхід: Tree - адреса кореня }
procedure LKP(Tree: PNode);
begin
if Tree = nil then Exit;
LKP(Tree^.left);
write(' ', Tree^.data);
LKP(Tree^.right);
end;
{ Процедура LKP – обхід дерева в порядку ЛКП
(лівий – корінь – правий)
Вхід: Tree - адреса кореня }
procedure LKP(Tree: PNode);
begin
if Tree = nil then Exit;
LKP(Tree^.left);
write(' ', Tree^.data);
LKP(Tree^.right);
end;
обхід цієї гілки
закінчено
обхід цієї гілки
закінчено
обхід лівого
піддерева
обхід лівого
піддерева
виведення даних
кореня
виведення даних
кореня
обхід правого
піддерева
обхід правого
піддерева
Для рекурсивної структури зручно
застосовувати рекурсивну обробку!
Для рекурсивної структури зручно
застосовувати рекурсивну обробку!
!!
93
Розбір арифметичних виразів
aa bb
++
++ 11
--
//
cc dd
a b + c d + 1 - /a b + c d + 1 - /
Як обчислювати автоматично:
Інфіксний запис, обхід ЛКП
(знак операції між операндами)
(a + b) / (c + d – 1)(a + b) / (c + d – 1)
необхідні дужки!
Постфіксний запис, ЛПК (знак операції після операндів)
a + b / c + d – 1a + b / c + d – 1
польська нотація,
Jan Łukasiewicz (1920)
польська нотація,
Jan Łukasiewicz (1920)
дужки не потрібні, можна однозначно обчислити!
Префіксний запис, КЛП (знак операції до операндів)
/ + a b - + c d 1/ + a b - + c d 1
зворотна польська нотація,
F. L. Bauer and E. W. Dijkstra
зворотна польська нотація,
F. L. Bauer and E. W. Dijkstra
94
Обчислення виразів
Постфіксна форма:
a b + c d + 1 - /a b + c d + 1 - /
Алгоритм:
1) взяти черговий елемент;
2) якщо це не знак операції, додати його в стек;
3) якщо це знак операції, то
• взяти з стека два операнди;
• виконати операцію і записати результат у стек;
1) перейти до кроку 1.
a
b
a a+b
c
a+b
d
c
a+b
c+d
a+b
1
c+d
a+b
c+d-1
a+b X
X =X =
95
Обчислення виразів
Завдання: в символьному рядку записаний правильний
арифметичний вираз, який може містити лише
однозначні числа і знаки операцій +-*. Обчислити
цей вираз.
Алгоритм:
1) ввести рядок;
2) побудувати дерево;
3) обчислити вираз по дереву.
Обмеження:
1) помилки не обробляються;
2) багатозначні числа не дозволені;
3) дробові числа не дозволені;
4) дужки не дозволені.
96
Побудова дерева
Алгоритм:
1) якщо first=last (залишився один символ – число), то
створити новій вузол і записати в нього цей елемент; інакше...
2) серед елементів віт first до last включно знайти останню
операцію (елемент з номером k);
3) створити новий вузол (корінь) і записати в нього знак операції;
4) рекурсивно застосувати цей алгоритм два рази:
• побудувати ліве піддерево, розібравши вираз з елементів
масиву з номерами від first до k-1;
• побудувати праве піддерево, розібравши вираз з
елементів масиву з номерами від k+1 до last.
5 + 7 * 6 - 3 * 2
firstfirst lastlastkk
k+1k+1k-1k-1
97
Як знайти останню операцію?
Порядок виконання операцій
• множення і ділення;
• додавання і віднімання.
5 + 7 * 6 - 3 * 2
Потрібно шукати останню операцію з
найменшим пріоритетом!
Потрібно шукати останню операцію з
найменшим пріоритетом!
!!
Пріоритет (старшинство) – число, що визначає
послідовність виконання операцій: раніше виконуються
операції з великим пріоритетом:
• множення і ділення (пріоритет 2);
• додавання і віднімання (пріоритет 1).
98
Пріоритет операції
{ Функція Priority – пріоритет операції
Вхід: символ операції
Вихід: пріоритет або 100, якщо не операція
}
function Priority ( c: char ): integer;
begin
case ( c ) of
'+', '-': Result := 1;
'*', '/': Result := 2;
else Result := 100;
end;
end;
{ Функція Priority – пріоритет операції
Вхід: символ операції
Вихід: пріоритет або 100, якщо не операція
}
function Priority ( c: char ): integer;
begin
case ( c ) of
'+', '-': Result := 1;
'*', '/': Result := 2;
else Result := 100;
end;
end;
додавання і
віднімання:
пріоритет 1
додавання і
віднімання:
пріоритет 1
множення і
ділення:
пріоритет 2
множення і
ділення:
пріоритет 2це взагалі не
операція
це взагалі не
операція
99
Номер останньої операції
{ Функція LastOperation – номер останньої операції
Вхід: рядок, номери першого і останнього
символів розглянутої частини
Вихід: номер символу - останньої операції }
function LastOperation ( Expr: string;
first, last: integer): integer;
var MinPrt, i, k, prt: integer;
begin
MinPrt := 100;
for i:=first to last do begin
prt := Priority ( Expr[i] );
if prt <= MinPrt then begin
MinPrt := prt;
k := i;
end;
end;
Result := k;
end;
{ Функція LastOperation – номер останньої операції
Вхід: рядок, номери першого і останнього
символів розглянутої частини
Вихід: номер символу - останньої операції }
function LastOperation ( Expr: string;
first, last: integer): integer;
var MinPrt, i, k, prt: integer;
begin
MinPrt := 100;
for i:=first to last do begin
prt := Priority ( Expr[i] );
if prt <= MinPrt then begin
MinPrt := prt;
k := i;
end;
end;
Result := k;
end;
перевіряємо
всі символи
перевіряємо
всі символи
повернути
номер символу
повернути
номер символу
знайшли операцію
з мінімальним
пріоритетом
знайшли операцію
з мінімальним
пріоритетом
100
Побудова дерева
Структура вузла
type PNode = ^Node;
Node = record
data: char;
left, right: PNode;
end;
type PNode = ^Node;
Node = record
data: char;
left, right: PNode;
end;
Створення вузла для числа (без нащадків)
function NumberNode(c: char): PNode;
begin
New(Result);
Result^.data := c;
Result^.left := nil;
Result^.right := nil;
end;
function NumberNode(c: char): PNode;
begin
New(Result);
Result^.data := c;
Result^.left := nil;
Result^.right := nil;
end;
повертає адресу
створено вузла
повертає адресу
створено вузла
один символ,
число
один символ,
число
101
Побудова дерева
{ Функція MakeTree – побудова дерева
Вхід: рядок, номери першого і останнього
символів розглянутої частини
Вихід: адреса побудованого дерева }
function MakeTree ( Expr: string;
first, last: integer): PNode;
var k: integer;
begin
if first = last then begin
Result := NumberNode ( Expr[first] );
Exit;
end;
k := LastOperation ( Expr, first, last );
New(Result);
Result^.data := Expr[k];
Result^.left := MakeTree ( Expr, first, k-1 );
Result^.right := MakeTree ( Expr, k+1, last );
end;
{ Функція MakeTree – побудова дерева
Вхід: рядок, номери першого і останнього
символів розглянутої частини
Вихід: адреса побудованого дерева }
function MakeTree ( Expr: string;
first, last: integer): PNode;
var k: integer;
begin
if first = last then begin
Result := NumberNode ( Expr[first] );
Exit;
end;
k := LastOperation ( Expr, first, last );
New(Result);
Result^.data := Expr[k];
Result^.left := MakeTree ( Expr, first, k-1 );
Result^.right := MakeTree ( Expr, k+1, last );
end;
залишилося
тільки число
залишилося
тільки число
новий вузол: операціяновий вузол: операція
102
Обчислення виразів по дереву
{ Функція CalcTree – обчислення по дереву
Вхід: адреса дерева
Вихід: значення виразу }
function CalcTree(Tree: PNode): integer;
var num1, num2: integer;
begin
if Tree^.left = nil then begin
Result := Ord(Tree^.data) - Ord('0');
Exit;
end;
num1 := CalcTree(Tree^.left);
num2 := CalcTree(Tree^.right);
case Tree^.data of
'+': Result := num1+num2;
'-': Result := num1-num2;
'*': Result := num1*num2;
'/': Result := num1 div num2;
else Result := MaxInt;
end;
end;
{ Функція CalcTree – обчислення по дереву
Вхід: адреса дерева
Вихід: значення виразу }
function CalcTree(Tree: PNode): integer;
var num1, num2: integer;
begin
if Tree^.left = nil then begin
Result := Ord(Tree^.data) - Ord('0');
Exit;
end;
num1 := CalcTree(Tree^.left);
num2 := CalcTree(Tree^.right);
case Tree^.data of
'+': Result := num1+num2;
'-': Result := num1-num2;
'*': Result := num1*num2;
'/': Result := num1 div num2;
else Result := MaxInt;
end;
end;
повернути число,
якщо це лист
повернути число,
якщо це лист
обчислюємо
операнди
(піддерева)
обчислюємо
операнди
(піддерева)
виконуємо
операцію
виконуємо
операцію
некоректна
операція
некоректна
операція
103
Основна програма
{ Введення і обчислення виразу за допомою
дерева }
program qq;
var Tree: PNode;
Expr: string;
{ процедури і функції }
begin
write('Введіть вираз > ');
readln( Expr );
Tree := MakeTree( Expr, 1, Length(Expr) );
writeln(' = ', CalcTree(Tree) );
end.
{ Введення і обчислення виразу за допомою
дерева }
program qq;
var Tree: PNode;
Expr: string;
{ процедури і функції }
begin
write('Введіть вираз > ');
readln( Expr );
Tree := MakeTree( Expr, 1, Length(Expr) );
writeln(' = ', CalcTree(Tree) );
end.
104
Дерево ігри
Завдання.
Перед двома гравцями лежать дві купки каміння, у першій з яких 3,
а в другій – 2 камені. У кожного гравця необмежено багато
каменів.
Гравці ходять по черзі. Хід полягає в том, що гравець або
збільшує в 3 рази кількість каменів в якійсь купі, або додає 1
камінь в якусь купу.
Виграє гравець, після ходу якого загальна кількість каменів у
двох купах стає не менше 16.
Хто виграє при безпомилковій грі – гравець, що робить перший
хід, чи гравець, який робить другий хід? Як повинен ходити
гравець що виграв?
105
Дерево ігри
3, 23, 2
гравецьгравець 11
3, 63, 6
27, 227, 227, 227, 2
3, 183, 183, 183, 18
3, 33, 3
4, 24, 2
12, 212, 2
4, 64, 6
5, 25, 2
4, 34, 3
9, 39, 3
4, 34, 3
3636,, 223636,, 22
44,, 181844,, 1818
1515,, 221515,, 22
12, 212, 2
4, 64, 6
5, 35, 3
4, 44, 4
3636, 2, 23636, 2, 2
1212,, 661212,, 66
1515,, 331515,, 33
1212,, 441212,, 44
27, 327, 327, 327, 3
При правильній грі виграє гравець 2гравець 2!При правильній грі виграє гравець 2гравець 2!
!!
гравецьгравець 11гравець 2гравець 2 гравецьгравець 22
9, 29, 2
4, 34, 3
4, 34, 3
ключовий
хід
ключовий
хід
виграв
гравець 1гравець 1
виграв
гравець 1гравець 1
106
Завдання
«4»: «Зібрати» програму для обчислення
правильного арифметичного виразу, що
включає тільки однозначні числа і знаки
операцій +, -, * , /.
«5»: Те ж саме, але допускаються також багатозначні
числа і дужки.
«6»: Те ж саме, що і в «5», але з обробкою помилок
(повинно виводитися повідомлення).
Тема 7. Графи
Динамічні
структури даних
(мова Паскаль)
© К.Ю. Поляков, 2008-2010
Переклад: Р. М. Васильчик
108
Визначення
Граф – це набір вершин (вузлів) і з'єднуючих їх ребер (дуг).
Спрямований граф (орієнтований, орграф) – це граф, в якому всі
дуги мають напрями.
Ланцюг – це послідовність ребер, що з'єднують дві вершини (в
орграфі – шлях).
Цикл – це ланцюг з якоїсь вершини в неї саму.
Зважений граф (мережа) – це граф, в якому кожному ребру
приписується вага (довжина).
5533
22
44
11
33
22
44
11
Дерево – це граф?Дерево – це граф?
??
Так, без циклів!Так, без циклів!
109
Визначення
Зв'язний граф – це граф, в якому існує ланцюг між кожною парою
вершин.
k-зв'язний граф – це граф, який можна розбити на k зв'язних
частин.
Повний граф – це граф, в якому проведено всі можливі ребра
(n вершин → n(n-1)/2 ребер).
5533
22
44
11
88
66
77
22
11
33
22
11
33
44
110
Опис графа
Матриця суміжності – це матриця, елемент M[i][j] якої
дорівнює 1, якщо існує ребро з вершини i в вершину j, і
дорівнює 0, якщо такого ребра немає.
5533
22
44
11 0 1 1 1 0
1 0 0 1 1
1 0 0 0 0
1 1 0 0 1
0 1 0 1 0
1 2 3 4 5
1
2
3
4
5
5533
22
44
11 0 1 1 1 0
0 0 0 1 1
0 0 0 0 0
0 0 0 0 1
0 0 0 0 0
1 2 3 4 5
1
2
3
4
5
Симетрія!Симетрія!
!!
Список суміжності
2 3 4
1 4 5
1
1 2 5
2 4
1
2
3
4
5
2 3 4
4 5
5
1
2
3
4
5
111
Матриця і список суміжності
1 2 3 4 5
1
2
3
4
5
1
2
3
4
5
2211
5533
44
1 2 3 4 5
1
2
3
4
5
1
2
3
4
5
11
5533
44
22
112
Побудова графа по матриці суміжності
0 0 1 0 0
0 0 1 0 1
1 1 0 1 0
0 0 1 0 1
0 1 0 1 0
1 2 3 4 5
1
2
3
4
5
0 0 1 1 1
0 1 0 1 0
0 1 0 1 0
1 1 0 0 0
0 1 1 0 0
1 2 3 4 5
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
11
33
55 22
44
11
33
55 22
44
113
Як виявити ланцюги і цикли?
Завдання: визначити, чи існує ланцюг довжини k з вершини i в
вершину j (або цикл довжиною k з вершини i в неї саму).
M2
[i][j]=1, если M[i][1]=1 і M[1][j]=1 або
M[i][2]=1 і M[2][j]=1 або
M[i][3]=1 і M[3][j]=1
рядок iрядок i
логічне
множення
логічне
множення
стовпець jстовпець j
логічне
додавання
логічне
додавання
11
33
44
22
0 0 1 0
1 0 0 0
0 1 0 1
1 0 0 0
1 2 3 4
1
2
3
4
M =
або
M[i][4]=1 і M[4][j]=1
114
Як виявити ланцюги і цикли?
M2
= M ⊗ MM2
= M ⊗ M
Логічне множення матриці на себе:
матриця шляхів
довжини 2
матриця шляхів
довжини 2
0 0 1 0
1 0 0 0
0 1 0 1
1 0 0 0
M2
= ⊗
0 0 1 0
1 0 0 0
0 1 0 1
1 0 0 0
=
0 1 0 1
0 0 1 0
11 0 0 0
0 0 1 0
11
33
44
22
1 2 3 4
1
2
3
4
M2
[3][1] = 0·0 + 1·1 + 0·0 + 1·1 = 1
маршрут 3-2-1маршрут 3-2-1 маршрут 3-4-1маршрут 3-4-1
115
Як виявити ланцюги і цикли?
M3
= M2
⊗ MM3
= M2
⊗ M
Матриця шляхів довжини 3:
0 0 1 0
1 0 0 0
0 1 0 1
1 0 0 0
M3
= ⊗ =
11 0 0 0
0 11 0 1
0 0 11 0
0 1 0 11
11
33
44
22
1 2 3 4
1
2
3
4
0 1 0 1
0 0 1 0
1 0 0 0
0 0 1 0
на головній
діагоналі –
цикли!
на головній
діагоналі –
цикли!
0 0 1 0
1 0 0 0
0 1 0 1
1 0 0 0
M4
= ⊗ =
00 00 11 00
11 00 00 00
00 11 00 11
11 00 00 00
1 2 3 4
1
2
3
4
1 0 0 0
0 1 0 1
0 0 1 0
0 1 0 1
116
Вагова матриця
5533
22
44
11
3
5
7
4
6
8
5533
22
44
11
3
5
7
4
6
8
Вагова матриця – це матриця, елемент W[i,j] якої дорівнює
вазі ребра з вершини i в вершину j (якщо воно є), або дорівнює
∞, якщо такого ребра немає.
0 7 3 5 ∞
7 0 ∞ 4 8
3 ∞ 0 ∞ ∞
5 4 ∞ 0 6
∞ 8 ∞ 6 0
1 2 3 4 5
1
2
3
4
5
0 7 3 5 ∞
∞ 0 ∞ 4 8
3 ∞ 0 ∞ ∞
5 ∞ ∞ 0 6
∞ 8 ∞ ∞ 0
1 2 3 4 5
1
2
3
4
5
117
Задача Прима-Краскала
Завдання: з'єднати N міст телефонною мережею так,
щоб довжина телефонних ліній була мінімальною.
Те ж завдання: дано зв'язний граф з N вершинами, веги
ребер задані ваговою матрицею W. Потрібно знайти
набір ребер, що з'єднує всі вершини графа (остовне
дерево) і має найменшу вагу.
5533
22
44
11
3
5
7
4
6
8
0 7 3 5 ∞
7 0 ∞ 4 8
3 ∞ 0 ∞ ∞
5 4 ∞ 0 6
∞ 8 ∞ 6 0
1 2 3 4 5
1
2
3
4
5
118
Жадібний алгоритм
Жадібний алгоритм – це багатокроковий алгоритм, в якому на
кожному кроці приймається рішення, краще в даний момент.
В цілому може вийти не оптимальне
рішення (послідовність кроків)!
В цілому може вийти не оптимальне
рішення (послідовність кроків)!
!!
Крок в задачі Прима-Краскала – це вибір ще невибраного ребра і
додавання його до рішення.
5533
22
44
11
3
5
7
4
6
8 В задачі Прима-Краскала
жадібний алгоритм дає
оптимальне рішення!
В задачі Прима-Краскала
жадібний алгоритм дає
оптимальне рішення!
!!
119
Реалізація алгоритму Прима-Краскала
Проблема: як перевірити, що
1) ребро не вибрано, і
2) ребро не утворює циклу з вибраними ребрами.
Рішення: присвоїти кожній вершині свій колір і перефарбовувати
вершини при додаванні ребра.
5533
22
44
1111
3
5
7
4
6
8
3333
2222
44 5555
Алгоритм:
1) пофарбувати всі вершини в різні кольори;
2) зробити N-1 раз в циклі:
 вибрати ребро (i,j) мінімальної довжини з усіх ребер,
що з'єднують вершини різного кольору;
 перефарбувати всі вершини, що мають колір j, в колір i.
1) вивести знайдені ребра.
4444
120
Реалізація алгоритму Прима-Краскала
Структура «ребро»:
type rebro = record
i, j: integer; { номери вершин }
end;
type rebro = record
i, j: integer; { номери вершин }
end;
const N = 5;
var W: array[1..N,1..N] of integer;
Color: array[1..N] of integer;
i, j, k, min, col_i, col_j: integer;
Reb: array[1..N-1] of rebro;
begin
... { тут треба ввести матрицю W }
for i:=1 to N do { розфарбувати в різні кольори }
Color[i] := i;
... { основний алгоритм – заповнення масиву Reb }
... { вивести знайдені ребра (масив Reb)}
end.
const N = 5;
var W: array[1..N,1..N] of integer;
Color: array[1..N] of integer;
i, j, k, min, col_i, col_j: integer;
Reb: array[1..N-1] of rebro;
begin
... { тут треба ввести матрицю W }
for i:=1 to N do { розфарбувати в різні кольори }
Color[i] := i;
... { основний алгоритм – заповнення масиву Reb }
... { вивести знайдені ребра (масив Reb)}
end.
Основна програма: вагова
матриця
вагова
матриця
колір
верши
н
колір
верши
н
121
Реалізація алгоритму Прима-Краскала
for k:=1 to N-1 do begin
min := MaxInt;
for i:=1 to N do
for j:=i+1 to N do
if (Color[i] <> Color[j]) and
(W[i,j] < min) then begin
min := W[i,j];
Reb[k].i := i;
Reb[k].j := j;
col_i := Color[i];
col_j := Color[j];
end;
for i:=1 to N do
if Color[i] = col_j then
Color[i] := col_i;
end;
for k:=1 to N-1 do begin
min := MaxInt;
for i:=1 to N do
for j:=i+1 to N do
if (Color[i] <> Color[j]) and
(W[i,j] < min) then begin
min := W[i,j];
Reb[k].i := i;
Reb[k].j := j;
col_i := Color[i];
col_j := Color[j];
end;
for i:=1 to N do
if Color[i] = col_j then
Color[i] := col_i;
end;
Основний алгоритм: потрібно вибрати
всього N-1 ребро
потрібно вибрати
всього N-1 ребро
цикл по всіх
парах вершин
цикл по всіх
парах вершин
враховуємо тільки
пари з різним
кольором вершин
враховуємо тільки
пари з різним
кольором вершин
запам'ятовуємо ребра
і кольори вершин
запам'ятовуємо ребра
і кольори вершин
перефарбовуєм
вершини кольору col_j
перефарбовуєм
вершини кольору col_j
122
Складність алгоритму
Основний цикл:
O(N3
)O(N3
)
for k:=1 to N-1 do begin
...
for i:=1 to N do
for j:=i+1 to N do
...
end;
for k:=1 to N-1 do begin
...
for i:=1 to N do
for j:=i+1 to N do
...
end;
три вкладених
цикли, в кожному
кількість кроків <=N
три вкладених
цикли, в кожному
кількість кроків <=N
зростає не швидше, ніж N3
Необхідна пам'ять:
var W: array[1..N,1..N] of integer;
Color: array[1..N] of integer;
Reb: array[1..N-1] of rebro;
var W: array[1..N,1..N] of integer;
Color: array[1..N] of integer;
Reb: array[1..N-1] of rebro;
O(N2
)O(N2
)
Кількість операцій:
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)
Pascal (динамічні структури даних)

More Related Content

What's hot

Мови програмування. Класифікація
Мови програмування. КласифікаціяМови програмування. Класифікація
Мови програмування. КласифікаціяAndy Levkovich
 
методи та прийоми навчання
методи та прийоми навчанняметоди та прийоми навчання
методи та прийоми навчанняSvetlanaLyashenko
 
Інформатика-5. Урок 5. Персональний комп'ютер та його складові
Інформатика-5. Урок 5. Персональний комп'ютер та його складовіІнформатика-5. Урок 5. Персональний комп'ютер та його складові
Інформатика-5. Урок 5. Персональний комп'ютер та його складовіВолодимир Бондар
 
5 клас урок 27 нова програма
5 клас урок 27 нова програма5 клас урок 27 нова програма
5 клас урок 27 нова програмаЮлія Артюх
 
Культура віртуального спілкування
Культура віртуального спілкуванняКультура віртуального спілкування
Культура віртуального спілкуванняПонкратова Людмила
 
Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...
Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...
Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...e-ranok e-ranok
 
Зберігання інформації. Носії інформації.
Зберігання інформації. Носії інформації.Зберігання інформації. Носії інформації.
Зберігання інформації. Носії інформації.V_Kobzar
 
3-4. Деревина та її властивості. Теорія - 5 клас
3-4. Деревина та її властивості. Теорія - 5 клас3-4. Деревина та її властивості. Теорія - 5 клас
3-4. Деревина та її властивості. Теорія - 5 класAndy Levkovich
 
Етапи створення веб сайтів
Етапи створення веб сайтівЕтапи створення веб сайтів
Етапи створення веб сайтівYulia Vlasenko
 
асоби створення, зберігання, обробки, копіювання і транспортування документів
асоби створення, зберігання, обробки, копіювання і транспортування документівасоби створення, зберігання, обробки, копіювання і транспортування документів
асоби створення, зберігання, обробки, копіювання і транспортування документівrussoua
 
Презентація:Комп"ютери та їх різновиди
Презентація:Комп"ютери та їх різновидиПрезентація:Комп"ютери та їх різновиди
Презентація:Комп"ютери та їх різновидиsveta7940
 
програмні засоби
програмні засобипрограмні засоби
програмні засобиNataKvasha
 
Презентація до 17 уроку в 8 класі
Презентація до 17 уроку в 8 класіПрезентація до 17 уроку в 8 класі
Презентація до 17 уроку в 8 класіЮлія Артюх
 
Презентація Бази даних Урок 1.pptx
Презентація Бази даних Урок 1.pptxПрезентація Бази даних Урок 1.pptx
Презентація Бази даних Урок 1.pptxssuserceb60a
 
Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...
Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...
Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...VsimPPT
 
1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів
1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів
1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктівAnatolii Barannik
 
Всесвітній День Землі
Всесвітній День ЗемліВсесвітній День Землі
Всесвітній День ЗемліInna Gornikova
 

What's hot (20)

Мови програмування. Класифікація
Мови програмування. КласифікаціяМови програмування. Класифікація
Мови програмування. Класифікація
 
методи та прийоми навчання
методи та прийоми навчанняметоди та прийоми навчання
методи та прийоми навчання
 
Інформатика-5. Урок 5. Персональний комп'ютер та його складові
Інформатика-5. Урок 5. Персональний комп'ютер та його складовіІнформатика-5. Урок 5. Персональний комп'ютер та його складові
Інформатика-5. Урок 5. Персональний комп'ютер та його складові
 
5 клас урок 27 нова програма
5 клас урок 27 нова програма5 клас урок 27 нова програма
5 клас урок 27 нова програма
 
Культура віртуального спілкування
Культура віртуального спілкуванняКультура віртуального спілкування
Культура віртуального спілкування
 
Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...
Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...
Хрестові походи ( за підручниками «Всесвітня історія ( авт. О. В. Гісем, О. О...
 
Зберігання інформації. Носії інформації.
Зберігання інформації. Носії інформації.Зберігання інформації. Носії інформації.
Зберігання інформації. Носії інформації.
 
3-4. Деревина та її властивості. Теорія - 5 клас
3-4. Деревина та її властивості. Теорія - 5 клас3-4. Деревина та її властивості. Теорія - 5 клас
3-4. Деревина та її властивості. Теорія - 5 клас
 
Етапи створення веб сайтів
Етапи створення веб сайтівЕтапи створення веб сайтів
Етапи створення веб сайтів
 
асоби створення, зберігання, обробки, копіювання і транспортування документів
асоби створення, зберігання, обробки, копіювання і транспортування документівасоби створення, зберігання, обробки, копіювання і транспортування документів
асоби створення, зберігання, обробки, копіювання і транспортування документів
 
Презентація:Комп"ютери та їх різновиди
Презентація:Комп"ютери та їх різновидиПрезентація:Комп"ютери та їх різновиди
Презентація:Комп"ютери та їх різновиди
 
програмні засоби
програмні засобипрограмні засоби
програмні засоби
 
Презентація до 17 уроку в 8 класі
Презентація до 17 уроку в 8 класіПрезентація до 17 уроку в 8 класі
Презентація до 17 уроку в 8 класі
 
4 клас урок 2 для чого потрібні файли та папки
4 клас урок 2 для чого потрібні файли та папки4 клас урок 2 для чого потрібні файли та папки
4 клас урок 2 для чого потрібні файли та папки
 
Презентація Бази даних Урок 1.pptx
Презентація Бази даних Урок 1.pptxПрезентація Бази даних Урок 1.pptx
Презентація Бази даних Урок 1.pptx
 
Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...
Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...
Урок 13 для 10 класу - Програмні засоби для складних обчислень, аналізу даних...
 
1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів
1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів
1.1 Поняття моделі. типи моделей. моделювання, як метод дослідження обєктів
 
Таблиці у Word
Таблиці у WordТаблиці у Word
Таблиці у Word
 
9 клас урок 14
9 клас урок 149 клас урок 14
9 клас урок 14
 
Всесвітній День Землі
Всесвітній День ЗемліВсесвітній День Землі
Всесвітній День Землі
 

Viewers also liked (8)

Pidsumki kvartal 2015 ua
Pidsumki kvartal 2015 uaPidsumki kvartal 2015 ua
Pidsumki kvartal 2015 ua
 
Mat Informator 10
Mat Informator 10Mat Informator 10
Mat Informator 10
 
Teoria ograniczeń - wprowadzenie - Manage or Die Inspiration
Teoria ograniczeń - wprowadzenie - Manage or Die InspirationTeoria ograniczeń - wprowadzenie - Manage or Die Inspiration
Teoria ograniczeń - wprowadzenie - Manage or Die Inspiration
 
Arystoteles O Duszy(1)[1]
Arystoteles   O Duszy(1)[1]Arystoteles   O Duszy(1)[1]
Arystoteles O Duszy(1)[1]
 
Pompa ciepła i kominek
Pompa ciepła i kominekPompa ciepła i kominek
Pompa ciepła i kominek
 
Lean Komunikacja
Lean KomunikacjaLean Komunikacja
Lean Komunikacja
 
Emocje
EmocjeEmocje
Emocje
 
Scalone dokumenty (20)
Scalone dokumenty (20)Scalone dokumenty (20)
Scalone dokumenty (20)
 

Similar to Pascal (динамічні структури даних)

Завдання олімпіади
Завдання олімпіадиЗавдання олімпіади
Завдання олімпіадиoksana oksana
 
Pascal osnovu2
Pascal osnovu2Pascal osnovu2
Pascal osnovu2Escuela
 
програмування на мові паскаль
програмування на мові паскаль програмування на мові паскаль
програмування на мові паскаль Helen Pata
 
Programuvanna na movi_pascal
Programuvanna na movi_pascalProgramuvanna na movi_pascal
Programuvanna na movi_pascalAnn Eres
 
Net framework і c# module 3
Net framework і c# module 3Net framework і c# module 3
Net framework і c# module 3Andrii Hladkyi
 
Programuvanna na movi_pascal
Programuvanna na movi_pascalProgramuvanna na movi_pascal
Programuvanna na movi_pascal1cana1
 
Основи програмування .Паскаль ч.1
Основи програмування .Паскаль ч.1Основи програмування .Паскаль ч.1
Основи програмування .Паскаль ч.1rznz
 
Pascal osnovu
Pascal osnovuPascal osnovu
Pascal osnovuEscuela
 
Mka python jr-urok_07_ua_1563258932
Mka python jr-urok_07_ua_1563258932Mka python jr-urok_07_ua_1563258932
Mka python jr-urok_07_ua_1563258932PavloTsiura
 
одновимірні масиви
одновимірні масивиодновимірні масиви
одновимірні масиви1cana1
 
Початок роботи в R: змінні, вектори та матриці
Початок роботи в R: змінні, вектори та матриціПочаток роботи в R: змінні, вектори та матриці
Початок роботи в R: змінні, вектори та матриціVladimir Bakhrushin
 

Similar to Pascal (динамічні структури даних) (20)

Завдання олімпіади
Завдання олімпіадиЗавдання олімпіади
Завдання олімпіади
 
Pascal osnovu2
Pascal osnovu2Pascal osnovu2
Pascal osnovu2
 
програмування на мові паскаль
програмування на мові паскаль програмування на мові паскаль
програмування на мові паскаль
 
Programuvanna na movi_pascal
Programuvanna na movi_pascalProgramuvanna na movi_pascal
Programuvanna na movi_pascal
 
Pascal основи програмування частина 1
Pascal основи програмування частина 1Pascal основи програмування частина 1
Pascal основи програмування частина 1
 
Net framework і c# module 3
Net framework і c# module 3Net framework і c# module 3
Net framework і c# module 3
 
Масиви
МасивиМасиви
Масиви
 
05 Arrays
05 Arrays05 Arrays
05 Arrays
 
Pascal основи програмування частина 2
Pascal основи програмування частина 2Pascal основи програмування частина 2
Pascal основи програмування частина 2
 
3018 1
3018 13018 1
3018 1
 
Povtor 7 8kl
Povtor 7 8klPovtor 7 8kl
Povtor 7 8kl
 
Programuvanna na movi_pascal
Programuvanna na movi_pascalProgramuvanna na movi_pascal
Programuvanna na movi_pascal
 
Основи програмування .Паскаль ч.1
Основи програмування .Паскаль ч.1Основи програмування .Паскаль ч.1
Основи програмування .Паскаль ч.1
 
Pascal osnovu
Pascal osnovuPascal osnovu
Pascal osnovu
 
Lecture 07 swift
Lecture 07 swiftLecture 07 swift
Lecture 07 swift
 
Паскаль
ПаскальПаскаль
Паскаль
 
Mka python jr-urok_07_ua_1563258932
Mka python jr-urok_07_ua_1563258932Mka python jr-urok_07_ua_1563258932
Mka python jr-urok_07_ua_1563258932
 
Less36
Less36Less36
Less36
 
одновимірні масиви
одновимірні масивиодновимірні масиви
одновимірні масиви
 
Початок роботи в R: змінні, вектори та матриці
Початок роботи в R: змінні, вектори та матриціПочаток роботи в R: змінні, вектори та матриці
Початок роботи в R: змінні, вектори та матриці
 

More from Володимир-Волинська загальноосвітня школа І-ІІІ ступенів №2

More from Володимир-Волинська загальноосвітня школа І-ІІІ ступенів №2 (20)

Портфоліо: Ліховська Наталія Леонідівна
Портфоліо: Ліховська Наталія ЛеонідівнаПортфоліо: Ліховська Наталія Леонідівна
Портфоліо: Ліховська Наталія Леонідівна
 
моральне кредо вчителів зарубіжної літератури і російської мови
моральне кредо вчителів зарубіжної літератури і російської мовиморальне кредо вчителів зарубіжної літератури і російської мови
моральне кредо вчителів зарубіжної літератури і російської мови
 
Учителі 1-х класів 2012-13
Учителі 1-х класів 2012-13Учителі 1-х класів 2012-13
Учителі 1-х класів 2012-13
 
Моральне кредо вчителів зарубіжної літератури і російської мови
Моральне кредо вчителів зарубіжної літератури і російської мовиМоральне кредо вчителів зарубіжної літератури і російської мови
Моральне кредо вчителів зарубіжної літератури і російської мови
 
Лизогуб З.Ф. презентація досвіду вчителя української мови та літератури ЗОШ №2
Лизогуб З.Ф. презентація досвіду вчителя української мови та літератури ЗОШ №2Лизогуб З.Ф. презентація досвіду вчителя української мови та літератури ЗОШ №2
Лизогуб З.Ф. презентація досвіду вчителя української мови та літератури ЗОШ №2
 
Володимир-Волинська загальноосвітня школа І-ІІІ ступенів №2
Володимир-Волинська загальноосвітня школа І-ІІІ ступенів №2Володимир-Волинська загальноосвітня школа І-ІІІ ступенів №2
Володимир-Волинська загальноосвітня школа І-ІІІ ступенів №2
 
Тема 17 (Компьютерні мережі)
Тема 17 (Компьютерні мережі)Тема 17 (Компьютерні мережі)
Тема 17 (Компьютерні мережі)
 
графіка
графікаграфіка
графіка
 
встановлення і видалення програм
встановлення і видалення програмвстановлення і видалення програм
встановлення і видалення програм
 
віруси та антивіруси
віруси та антивірусивіруси та антивіруси
віруси та антивіруси
 
використання ресурсів мережі інтернет
використання ресурсів мережі інтернетвикористання ресурсів мережі інтернет
використання ресурсів мережі інтернет
 
антивірус касперського
антивірус касперськогоантивірус касперського
антивірус касперського
 
File system7
File system7File system7
File system7
 
Coreldraw
CoreldrawCoreldraw
Coreldraw
 
01 03-інформаційні системи та технології
01 03-інформаційні системи та технології01 03-інформаційні системи та технології
01 03-інформаційні системи та технології
 
01 03-інформаційні системи та технології
01 03-інформаційні системи та технології01 03-інформаційні системи та технології
01 03-інформаційні системи та технології
 
01 02-кодування інформації
01 02-кодування інформації01 02-кодування інформації
01 02-кодування інформації
 
характеристика основних вузщлів компютера
характеристика основних вузщлів компютерахарактеристика основних вузщлів компютера
характеристика основних вузщлів компютера
 
форматування та редагування тексту
форматування та редагування текстуформатування та редагування тексту
форматування та редагування тексту
 
тр прийоми редагування текстів
тр прийоми редагування текстівтр прийоми редагування текстів
тр прийоми редагування текстів
 

Recently uploaded

Автомат.звука с.інтегровані ігри для дітейpptx
Автомат.звука с.інтегровані ігри для дітейpptxАвтомат.звука с.інтегровані ігри для дітейpptx
Автомат.звука с.інтегровані ігри для дітейpptxvitalina6709
 
upd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdf
upd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdfupd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdf
upd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdfssuser54595a
 
О.Духнович - пророк народної правди. Біографія
О.Духнович - пророк народної правди. БіографіяО.Духнович - пророк народної правди. Біографія
О.Духнович - пророк народної правди. БіографіяAdriana Himinets
 
Відкрита лекція на тему «Біологічний захист рослин у теплицях»
Відкрита лекція на тему «Біологічний захист рослин у теплицях»Відкрита лекція на тему «Біологічний захист рослин у теплицях»
Відкрита лекція на тему «Біологічний захист рослин у теплицях»tetiana1958
 

Recently uploaded (6)

Автомат.звука с.інтегровані ігри для дітейpptx
Автомат.звука с.інтегровані ігри для дітейpptxАвтомат.звука с.інтегровані ігри для дітейpptx
Автомат.звука с.інтегровані ігри для дітейpptx
 
upd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdf
upd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdfupd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdf
upd.18-04-UA_REPORT_MEDIALITERAСY_INDEX-DM_23_FINAL.pdf
 
Віртуальна виставка «Аграрна наука України у виданнях: історичний аспект»
Віртуальна виставка «Аграрна наука України у виданнях: історичний аспект»Віртуальна виставка «Аграрна наука України у виданнях: історичний аспект»
Віртуальна виставка «Аграрна наука України у виданнях: історичний аспект»
 
О.Духнович - пророк народної правди. Біографія
О.Духнович - пророк народної правди. БіографіяО.Духнович - пророк народної правди. Біографія
О.Духнович - пророк народної правди. Біографія
 
Відкрита лекція на тему «Біологічний захист рослин у теплицях»
Відкрита лекція на тему «Біологічний захист рослин у теплицях»Відкрита лекція на тему «Біологічний захист рослин у теплицях»
Відкрита лекція на тему «Біологічний захист рослин у теплицях»
 
Її величність - українська книга презентація-огляд 2024.pptx
Її величність - українська книга презентація-огляд 2024.pptxЇї величність - українська книга презентація-огляд 2024.pptx
Її величність - українська книга презентація-огляд 2024.pptx
 

Pascal (динамічні структури даних)

  • 1. Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик 1. Вказівники 2. Динамічні масиви 3. Структури 4. Списки 5. Стеки, черги, деки 6. Дерева 7. Графи
  • 2. Тема 1. Вказівники Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 3. 3 Статині дані • змінна (масив) має ім'я, за яким до неї можна звертатися • розмір наперед відомий (задається при написанні програми) • пам'ять виділяється при оголошенні • розмір не можна збільшити під час роботи програми var x, y: integer; z: real; A: array[1..10] of real; str: string; var x, y: integer; z: real; A: array[1..10] of real; str: string;
  • 4. 4 Динамічні дані • розмір наперед невідомий, визначається під час роботи програми • пам'ять виділяється під час роботи програми • немає імені? Проблема: як звертатися до даних, якщо немає імені? Рішення: використовувати адресу в пам'яті Наступна проблема: в яких змінних може міститися адреса? як працювати з адресами?
  • 5. 5 Вказівники Вказівник – це змінна, в яку можна записати адресу другої змінної (або блоку пам'яті). Оголошення: Як записати адресу: var pC: ^char; // адреса символу pI: ^integer; // адреса цілої змінної pR: ^real; // адреса дійсн. змінної var pC: ^char; // адреса символу pI: ^integer; // адреса цілої змінної pR: ^real; // адреса дійсн. змінної var m: integer; // ціла змінна pI: ^integer; // вказівник A: array[1..2] of integer; // масив ... pI:= @ m; // адреса змінної m pI:= @ A[1]; // адреса елемента масиву A[1] pI:= nil; // нульова адреса var m: integer; // ціла змінна pI: ^integer; // вказівник A: array[1..2] of integer; // масив ... pI:= @ m; // адреса змінної m pI:= @ A[1]; // адреса елемента масиву A[1] pI:= nil; // нульова адреса @ ^ nil вказівниквказівник адреса коміркиадреса комірки
  • 6. 6 Звернення до даних через вказівник program qq; var m, n: integer; pI: ^integer; begin m := 4; pI := @m; writeln('Адрес m = ', pI);//виведення адреси writeln('m = ', pI^); // виведення значення n := 4*(7 - pI^); // n = 4*(7 - 4) = 12 pI^ := 4*(n - m); // m = 4*(12 – 4) = 32 end. program qq; var m, n: integer; pI: ^integer; begin m := 4; pI := @m; writeln('Адрес m = ', pI);//виведення адреси writeln('m = ', pI^); // виведення значення n := 4*(7 - pI^); // n = 4*(7 - 4) = 12 pI^ := 4*(n - m); // m = 4*(12 – 4) = 32 end. ^ «витягнути» значення за адресою «витягнути» значення за адресою
  • 7. 7 Звернення до даних (масиви) program qq; var i: integer; A: array[1..4] of integer; pI: ^integer; begin for i:=1 to 4 do A[i] := i; pI := @A[1]; // адреса A[1] while ( pI^ <= 4 ) // while( A[i] <= 4 ) do begin pI^ := pI^ * 2; // A[i] := A[i]*2; pI := pI + 1; // до наступного елемента end; end. program qq; var i: integer; A: array[1..4] of integer; pI: ^integer; begin for i:=1 to 4 do A[i] := i; pI := @A[1]; // адреса A[1] while ( pI^ <= 4 ) // while( A[i] <= 4 ) do begin pI^ := pI^ * 2; // A[i] := A[i]*2; pI := pI + 1; // до наступного елемента end; end. переміститися до наступного елементу = змінити адресу на sizeof(integer) переміститися до наступного елементу = змінити адресу на sizeof(integer) Не працює в PascalABC.NET! Не працює в PascalABC.NET! !!
  • 8. 8 Що потрібно знати про вказівники • вказівник – це змінна, в якій можна зберігати адресу іншої змінної; • при оголошенні вказівника потрібно вказувати тип змінної, на який він буде вказувати, а перед типом поставити знак ^ ; • знак @ перед іменем змінної означає її адресу; • запис p^ означає значення комірки, на яку вказує вказівник p; • nil – це нульовий вказівник, він нікуди не показує • при зміні значення вказівника на n він насправді зміщується до n-го наступного числа даного типу (для вказівника на цілі числа – на n*sizeof(integer) байт). Не можна використовувати вказівники, які показують невідомо куди (буде збій або зависання)!
  • 9. Тема 2. Динамічні масиви Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 10. 10 Де потрібні динамічні масиви? Задача. Ввести розмір масиву, потім – елементи масиву. Відсортувати масив і вивести на екран. Проблема: розмір масиву наперед невідомий. Шляхи розв'язання: 1) виділити пам’ять «з запасом»; 2) виділяємо пам’ять тоді, коли розмір став відомий. Алгоритм: 1) ввести розмір масиву; 2) виділити пам’ять; 3) ввести елементи масиву; 4) відсортувати і вивести на екран; 5) знищити масив. виділити пам’ятьвиділити пам’ять знищити масивзнищити масив
  • 11. 11 Використовування вказівників (Delphi) program qq; type intArray = array[1..1] of integer; var A: ^intArray; i, N: integer; begin writeln('Розмір масиву>'); readln(N); GetMem(pointer(A), N*sizeof(integer)); for i := 1 to N do readln(A[i]); ... { сортування } for i := 1 to N do writeln(A[i]); FreeMem(pointer(A)); end. виділяємо пам’ятьвиділяємо пам’ять вивільняємо пам’ятьвивільняємо пам’ять працюємо так само, як з звичайним масивом! працюємо так само, як з звичайним масивом! деякий масив цілих чиселдеякий масив цілих чисел
  • 12. 12 Використання вказівників • для виділення пам’яті використовується процедура GetMem GetMem( вказівник, розмір в байтах ); • вказівник повинен бути прив'язаний до типу pointer– вказівник без типу, просто адреса деякого байта в пам'яті; • з динамічним масивом можна працювати так само, як і з звичайним (статичним); • для вивільнення блоку пам'яті потрібно застосувати процедуру FreeMem: FreeMem ( вказівник );
  • 13. 13 Помилки при роботі з пам’ятю Запис в «чужу» область пам'яті: пам'ять не була виділена, а масив використовується. Що робити: так не робити. Вихід за границі масиву: звернення до елементу масиву з неправильним номером, при запису псуються дані в «чужій» пам’яті. Що робити: якщо дозволяє транслятор, включати перевірку виходу за границі масиву. Вказівник знищується другий раз: структура пам’яті порушена, може бути все, що завгодно. Що робити : в видалений вказівник краще записати nil, помилка виявиться швидше. Витік пам'яті: непотрібна пам’ять не вивільняється. Що робити : прибирайте «сміття» (в середовищі .NET є збирач сміття!)
  • 14. 14 Динамічні масиви (Delphi) program qq; var A: array of integer; i, N: integer; begin writeln(‘Розмір масиву>'); readln(N); SetLength ( A, N ); for i := 0 to N-1 do readln(A[i]); ... { сортування } for i := 0 to N-1 do writeln(A[i]); SetLength( A, 0 ); end. виділяємо пам’ятьвиділяємо пам’ять вивільняємо пам’ятьвивільняємо пам’ять деякий масив цілих чисел деякий масив цілих чисел нумеруємо з НУЛЯ!нумеруємо з НУЛЯ!
  • 15. 15 Динамічні масиви (Delphi) • при оголошенні масиву вказується тільки його тип, пам’ять не виділяється: var A: array of integer; • для виділення пам’яті використовується процедура SetLength (встановити довжину) SetLength ( масив, розмір ); • номери елементів починаються з НУЛЯ! • для вивільнення блоку пам’яті потрібно встановити нульову довжину через процедуру SetLength: SetLength ( масив, 0 );
  • 16. 16 Динамічні матриці (Delphi) Задача. Ввести розмір матриці і виділити для неї місце в пам’яті під час роботи програми. Проблема: розміри матриці наперед невідомі Розв'язання: var A: array of array of integer; N, M: integer; begin writeln(‘Кількість рядків і стовпців>'); readln(N, M); SetLength ( A, N, M ); ... // працюємо, як з звичайною матрицею SetLength( A, 0, 0 ); end. var A: array of array of integer; N, M: integer; begin writeln(‘Кількість рядків і стовпців>'); readln(N, M); SetLength ( A, N, M ); ... // працюємо, як з звичайною матрицею SetLength( A, 0, 0 ); end.
  • 17. Тема 3. Структури (записи) Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 18. 18 Структури (в Паскалі – записи) Структура (запис) – це тип даних, які можуть включати в себе декілька полів – елементів різних типів (в том числі і інші структури). Властивості: • автор (рядок) • назва (рядок) • рік видання (ціле число) • кількість сторінок (ціле число) Задача: об'єднати ці дані в одне ціле Задача: об'єднати ці дані в одне ціле Розміщення в пам’яті автор назва рік видання кількість сторінок 40 символів 80 символів ціле ціле
  • 19. 19 Один запис readln(Book.author); // введення readln(Book.title); Book.year := 1998; // присвоєння if Book.pages > 200 then // порівняння writeln(Book.author,'.',Book.title);// виведення readln(Book.author); // введення readln(Book.title); Book.year := 1998; // присвоєння if Book.pages > 200 then // порівняння writeln(Book.author,'.',Book.title);// виведення Оголошення (виділення пам’яти): var Book: record author: string[40]; // автор, рядок title: string[80]; // назва, рядок year: integer; // рік видання, ціле pages: integer; // кількість сторінок, ціле end; var Book: record author: string[40]; // автор, рядок title: string[80]; // назва, рядок year: integer; // рік видання, ціле pages: integer; // кількість сторінок, ціле end; названазва записзапис поляполя Звернення до полів: Для звернення до поля запису використовується крапка! Для звернення до поля запису використовується крапка! !!
  • 20. 20 Масив записів Оголошення (виділення пам’яті): const N = 10; var aBooks: array[1..N] of record author: string[40]; title: string[80]; year: integer; pages: integer; end; const N = 10; var aBooks: array[1..N] of record author: string[40]; title: string[80]; year: integer; pages: integer; end; Books[1] ... Books[10] author title year pages
  • 21. 21 Масив записів for i:=1 to N do begin readln(aBooks[i].author); readln(aBooks[i].title); ... end; for i:=1 to N do if aBooks[i].pages > 200 then writeln(aBooks[i].author, '.', aBooks[i].title); for i:=1 to N do begin readln(aBooks[i].author); readln(aBooks[i].title); ... end; for i:=1 to N do if aBooks[i].pages > 200 then writeln(aBooks[i].author, '.', aBooks[i].title); Звернення до поля: aBooks[i].author – звернення до поля author запису aBooks[i] aBooks[i].author – звернення до поля author запису aBooks[i] !!
  • 22. 22 Новий тип даних – запис const N = 10; var Book: TBook; // один запис aBooks: array[1..N] of TBook; // масив const N = 10; var Book: TBook; // один запис aBooks: array[1..N] of TBook; // масив Оголошення типу: type TBook = record author: string[40]; // автор, рядок title: string[80]; // назва, рядок year: integer;// рік видання, ціле pages : integer; // кількість сторінок, ціле end; type TBook = record author: string[40]; // автор, рядок title: string[80]; // назва, рядок year: integer;// рік видання, ціле pages : integer; // кількість сторінок, ціле end; Пам'ять не виділяється!Пам'ять не виділяється! !! Оголошення змінних і масивів: TBook – Type Book («тип книга») – зручно!
  • 23. 23 Записи в процедурах і функціях Book.author := ‘О.С. Пушкін'; ShowAuthor ( Book ); Book.year := 1800; writeln( IsOld(Book) ); Book.author := ‘О.С. Пушкін'; ShowAuthor ( Book ); Book.year := 1800; writeln( IsOld(Book) ); Процедура: procedure ShowAuthor ( b: TBook ); begin writeln ( b.author ); end; procedure ShowAuthor ( b: TBook ); begin writeln ( b.author ); end; Основна програма: function IsOld( b: TBook ): boolean; begin IsOld := b.year < 1900; end; function IsOld( b: TBook ): boolean; begin IsOld := b.year < 1900; end; Функція:
  • 24. 24 Файли записів Оголошення вказівника на файл: var F: file of TBook;var F: file of TBook; Assign(F, 'books.dat');{ зв'язати з вказівником } Rewrite(F); { відкрити файл для запису } writeln(F, Book); { запис } for i:=1 to 5 do writeln(aBook[i]); { запис } Close(F); { закрити файл } Assign(F, 'books.dat');{ зв'язати з вказівником } Rewrite(F); { відкрити файл для запису } writeln(F, Book); { запис } for i:=1 to 5 do writeln(aBook[i]); { запис } Close(F); { закрити файл } Запис у файл:
  • 25. 25 Читання з файлу Відома кількість записів: Assign(F, 'books.dat');{ зв'язати з вказівником } Reset(F); { відкрити для читання } Read(F, Book); { читання } for i:=1 to 5 do Read(F, aBook[i]); { читання } Close(F); { закрити файл } Assign(F, 'books.dat');{ зв'язати з вказівником } Reset(F); { відкрити для читання } Read(F, Book); { читання } for i:=1 to 5 do Read(F, aBook[i]); { читання } Close(F); { закрити файл } «Поки не закінчується»: count := 0; while not eof(F) do begin count := count + 1; { лічильник } Read(F, aBook[count]); { читання } end; count := 0; while not eof(F) do begin count := count + 1; { лічильник } Read(F, aBook[count]); { читання } end; В чому може бути проблема!В чому може бути проблема! ?? поки не дійшли до кінця файлу F EOF = end of file поки не дійшли до кінця файлу F EOF = end of file
  • 26. 26 Приклад програми Задача: в файлі books.dat записані дані про книги у вигляді масиву структур типу TBook (не більше 100). Встановити для всіх 2008 рік видання і записати назад в той самий файл. type Tbook … ; const MAX = 100; var aBooks: array[1..MAX] of TBook; i, N: integer; F: file of TBook; begin { прочитати записи з файлу, N - кількість } for i:=1 to N do aBooks[i].year := 2008; { зберегти у файл } end. type Tbook … ; const MAX = 100; var aBooks: array[1..MAX] of TBook; i, N: integer; F: file of TBook; begin { прочитати записи з файлу, N - кількість } for i:=1 to N do aBooks[i].year := 2008; { зберегти у файл } end. type TBook … ; повне описання структури повне описання структури
  • 27. 27 Приклад програми Читання «поки не закінчиться»: Assign(f, 'books.dat'); Reset(f); N := 0; while not eof(F) and (N < MAX) do begin N := N + 1; read(F, aBooks[N]); end; Сlose(f); Assign(f, 'books.dat'); Reset(f); N := 0; while not eof(F) and (N < MAX) do begin N := N + 1; read(F, aBooks[N]); end; Сlose(f); Assign(f, 'books.dat'); { можна без цього } Rewrite(f); for i:=1 to N do write(F, aBooks[i]); Close(f); Assign(f, 'books.dat'); { можна без цього } Rewrite(f); for i:=1 to N do write(F, aBooks[i]); Close(f); Збереження: що б не вийти за межі масиву що б не вийти за межі масиву
  • 28. 28 Виділення пам'яті під запис var pB: ^TBook; begin New(pB); pB^.author := ‘О.С. Пушкін'; pB^.title := 'Полтава'; pB^.year := 1990; pB^.pages := 129; Dispose(pB); end. var pB: ^TBook; begin New(pB); pB^.author := ‘О.С. Пушкін'; pB^.title := 'Полтава'; pB^.year := 1990; pB^.pages := 129; Dispose(pB); end. для звернення до поля запису за адресою використовується знак ^ для звернення до поля запису за адресою використовується знак ^ !! New(pB); виділення пам'яті під запис, записати адресу в pB виділення пам'яті під запис, записати адресу в pB pB^ Dispose(pB); вивільнити пам’ять вивільнити пам’ять pB: ^TBook; змінна - вказівник на TBook змінна - вказівник на TBook
  • 29. 29 Сортування масиву записів Ключ (ключове поле) – це поле запису (або комбінація полів), за яким виконується сортування. const N = 100; var aBooks: array[1..N] of TBook; i, j, N: integer; temp: TBook; { для обміну } begin { заповнити масив aBooks } { відсортувати = переставити } for i:=1 to N do writeln(aBooks[i].title, aBooks[i].year:5); end. const N = 100; var aBooks: array[1..N] of TBook; i, j, N: integer; temp: TBook; { для обміну } begin { заповнити масив aBooks } { відсортувати = переставити } for i:=1 to N do writeln(aBooks[i].title, aBooks[i].year:5); end.
  • 30. 30 Сортування масиву записів for i:=1 to N-1 do for j:=N-1 downto i do if aBooks[j].year > aBooks[j+1].year then begin temp := aBooks[j]; aBooks[j] := aBooks[j+1]; aBooks[j+1] := temp; end; for i:=1 to N-1 do for j:=N-1 downto i do if aBooks[j].year > aBooks[j+1].year then begin temp := aBooks[j]; aBooks[j] := aBooks[j+1]; aBooks[j+1] := temp; end; Який ключ сортування?Який ключ сортування? ?? Який метод сортування?Який метод сортування? ?? Що погано?Що погано? ??
  • 31. 31 Сортування масиву записів Проблема: як уникнути копіювання запису при сортуванні? Рішення: використовувати допоміжний масив вказівників, при сортуванні переставляти вказівники. 5 1 3 2 4 p[1] p[2] p[3] p[4] p[5] p[5] p[1] p[3] p[2] p[4] До сортування: Після сортування: Виведення результату: for i:=1 to N do writeln(p[i]^.title, p[i]^.year:5); for i:=1 to N do writeln(p[i]^.title, p[i]^.year:5);p[i]^ p[i]^ 5 1 3 2 4
  • 32. 32 Реалізація в програмі type PBook = ^TBook; { новий тип даних } var p: array[1..N] of PBook; begin { заповнення масиву записів} for i:=1 to N do p[i] := @aBooks[i]; for i:=1 to N do writeln(p[i]^.title, p[i]^.year:5); end. type PBook = ^TBook; { новий тип даних } var p: array[1..N] of PBook; begin { заповнення масиву записів} for i:=1 to N do p[i] := @aBooks[i]; for i:=1 to N do writeln(p[i]^.title, p[i]^.year:5); end. for i:=1 to N-1 do for j:=N-1 downto i do if p[j]^.year > p[j+1]^.year then begin temp := p[j]; p[j] := p[j+1]; p[j+1] := temp; end; допоміжні вказівники допоміжні вказівники міняємо тільки вказівники, записи залишаються на місцях міняємо тільки вказівники, записи залишаються на місцях початкова розстановка початкова розстановка
  • 33. Тема 4. Списки Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 34. 34 Динамічні структури даних Будова: набір вузлів, об'єднаних за допомогою посилань. Як влаштований вузол: дані посилання на інші вузли Типи структур: списки дерева графи nil nilnil однозв'язні двонаправлені (двозв'язні) циклічні списки (кільця) nil nil nil nil nil nil
  • 35. 35 Коли потрібні списки? Завдання (алфавітно-частотний словник). В файлі записаний текст. Потрібно записати в інший файл в стовпчик всі слова, що зустрічаються в тексті, в алфавітному порядку, і кількість повторень для кожного слова. Проблеми: 1) кількість слів заздалегідь невідомо (статичний масив); 2) кількість слів визначається тільки в кінці роботи (динамічний масив). Рішення – список. Алгоритм: 1) створити список; 2) якщо слова в файлі закінчилися, то стоп. 3) прочитати слово и шукати його у списку; 4) якщо слово знайдено – збільшити лічильник повторень, інакше додати слово до списку; 5) перейти до кроку 2.
  • 36. 36 Що таке список: 1) порожня структура – це список; 2) список – це початковий вузол (голова) і пов'язаний з ним список. Списки: нові типи даних type PNode = ^Node; { вказівник на вузол } Node = record { структура вузла } word: string[40];{ слово } count: integer; { лічильник повторень } next: PNode; {посилання на наступний} end; type PNode = ^Node; { вказівник на вузол } Node = record { структура вузла } word: string[40];{ слово } count: integer; { лічильник повторень } next: PNode; {посилання на наступний} end; Нові типи даних: Адреса початку списку: var Head: PNode; ... Head := nil; var Head: PNode; ... Head := nil; Рекурсивне визначення! Рекурсивне визначення!!! nil Для доступу до списку достатньо знати адресу його голови! Для доступу до списку достатньо знати адресу його голови! !!
  • 37. 37 Що потрібно вміти робити зі списком? 1. Створити новий вузол. 2. Добавити вузол: а) в початок списку; б) в кінець списку; в) після заданого вузла; г) до заданого вузла. 3. Шукати потрібний вузол в списку. 4. Видалити вузол.
  • 38. 38 Створення вузла function CreateNode(NewWord: string): PNode; var NewNode: PNode; begin New(NewNode); NewNode^.word := NewWord; NewNode^.count := 1; NewNode^.next := nil; Result := NewNode; end; function CreateNode(NewWord: string): PNode; var NewNode: PNode; begin New(NewNode); NewNode^.word := NewWord; NewNode^.count := 1; NewNode^.next := nil; Result := NewNode; end; Функція CreateNode (створити вузол): вхід: нове слово, прочитане з файлу; вихід: адреса нового вузла, створеного в пам'яті. повертає адресу створеного вузла повертає адресу створеного вузланове словонове слово Якщо пам'ять виділити не вдалося? Якщо пам'ять виділити не вдалося? ??
  • 39. 39 Добавлення вузла в початок списку NewNodeNewNode HeadHead nil 1) Встановити посилання нового вузла на голову списку: NewNode^.next := Head;NewNode^.next := Head;NewNodeNewNode HeadHead nil nil 2) Встановити новий вузол як голову списку: Head := NewNode;Head := NewNode; procedure AddFirst ( var Head: PNode; NewNode: PNode ); begin NewNode^.next := Head; Head := NewNode; end; procedure AddFirst ( var Head: PNode; NewNode: PNode ); begin NewNode^.next := Head; Head := NewNode; end; var адреса голови міняєтьсяадреса голови міняється
  • 40. 40 Добавлення вузла після заданого 1) Встановити посилання нового вузла на вузол, наступний за p: NewNode^.next = p^.next;NewNode^.next = p^.next; 2) Встановити посилання вузла p на новий вузол: p^.next = NewNode;p^.next = NewNode; NewNodeNewNode pp nil nil NewNodeNewNode pp nil procedure AddAfter ( p, NewNode: PNode ); begin NewNode^.next := p^.next; p^.next := NewNode; end; procedure AddAfter ( p, NewNode: PNode ); begin NewNode^.next := p^.next; p^.next := NewNode; end;
  • 41. 41 Завдання: зробити що-небудь хороше з кожним елементом списку. Алгоритм: 1) встановити допоміжний вказівник q на голову списку; 2) якщо вказівник q дорівнює nil (дійшли до кінця списку), то стоп; 3) виконати дію над вузлом з адресою q ; 4) перейти до наступного вузла, q^.next. Прохід по списку var q: PNode; ... q := Head; // почали з голови while q <> nil do begin // поки не дійшли до кінця ... // робимо щось хороше з q q := q^.next; // переходимо до наступного end; var q: PNode; ... q := Head; // почали з голови while q <> nil do begin // поки не дійшли до кінця ... // робимо щось хороше з q q := q^.next; // переходимо до наступного end; HeadHead nil qq
  • 42. 42 Добавлення вузла в кінець списку Завдання: додати новий вузол в кінець списку. Алгоритм: 1) знайти останній вузол q, такий що q^.next дорівнює nil; 2) добавити вузол після вузла з адресою q (процедура AddAfter). Особливий випадок: добавлення в порожній список. procedure AddLast ( var Head: PNode; NewNode: PNode ); var q: PNode; begin if Head = nil then AddFirst ( Head, NewNode ) else begin q := Head; while q^.next <> nil do q := q^.next; AddAfter ( q, NewNode ); end; end; procedure AddLast ( var Head: PNode; NewNode: PNode ); var q: PNode; begin if Head = nil then AddFirst ( Head, NewNode ) else begin q := Head; while q^.next <> nil do q := q^.next; AddAfter ( q, NewNode ); end; end; особливий випадок – добавлення в порожній список особливий випадок – добавлення в порожній список шукаємо останній вузолшукаємо останній вузол добавити вузол після вузла q добавити вузол після вузла q
  • 43. 43 Проблема: потрібно знати адресу попереднього вузла, а йти назад не можна! Рішення: знайти попередній вузол q (прохід з початку списку). Добавлення вузла перед заданим NewNodeNewNode pp nil nil procedure AddBefore(var Head: PNode; p, NewNode: PNode); var q: PNode; begin q := Head; if p = Head then AddFirst ( Head, NewNode ) else begin while (q <> nil) and (q^.next <> p) do q := q^.next; if q <> nil then AddAfter ( q, NewNode ); end; end; procedure AddBefore(var Head: PNode; p, NewNode: PNode); var q: PNode; begin q := Head; if p = Head then AddFirst ( Head, NewNode ) else begin while (q <> nil) and (q^.next <> p) do q := q^.next; if q <> nil then AddAfter ( q, NewNode ); end; end; в початок спискув початок списку шукаємо вузол, наступний за яким - вузол p шукаємо вузол, наступний за яким - вузол p добавити вузол після вузла q добавити вузол після вузла q Що погано?Що погано???
  • 44. 44 Добавлення вузла перед заданим (II) Завдання: вставити вузол перед заданим без пошуку попереднього. Алгоритм: 1) поміняти місцями дані нового вузла і вузла p; 2) встановити посилання вузла p на NewNode. procedure AddBefore2 ( p, NewNode: PNode ); var temp: Node; begin temp := p^; p^ := NewNode^; NewNode^ := temp; p^.next := NewNode; end; procedure AddBefore2 ( p, NewNode: PNode ); var temp: Node; begin temp := p^; p^ := NewNode^; NewNode^ := temp; p^.next := NewNode; end; NewNodeNewNode pp nil Так не можна, якщо p = nil або адреси вузлів десь ще запам'ятовуються! Так не можна, якщо p = nil або адреси вузлів десь ще запам'ятовуються! !! NewNodeNewNode pp nil nil
  • 45. 45 Пошук слова в списку Завдання: знайти в списку задане слово або визначити, що його немає. Функція Find: вхід: слово (символьний рядок); вихід: адреса вузла, з цим словом або nil. Алгоритм: прохід по списку. function Find(Head: PNode; NewWord: string): PNode; var q: PNode; begin q := Head; while (q <> nil) and (NewWord <> q^.word) do q := q^.next; Result := q; end; function Find(Head: PNode; NewWord: string): PNode; var q: PNode; begin q := Head; while (q <> nil) and (NewWord <> q^.word) do q := q^.next; Result := q; end; шукаємо це словошукаємо це слово результат – адреса вузла або nil (нема такого) результат – адреса вузла або nil (нема такого) while (q <> nil) and (NewWord <> q^.word) do q := q^.next; поки не дійшли до кінця списку і слово не дорівнює заданому поки не дійшли до кінця списку і слово не дорівнює заданому
  • 46. 46 Куди вставити нове слово? Завдання: знайти вузол, перед яким потрібно вставити, задане слово, так щоб у списку зберігався алфавітний порядок слів. Функція FindPlace: вхід: слово (символьний рядок); вихід: адреса вузла, перед яким потрібно вставити це слово або nil, якщо слово потрібно вставити в кінець списку. function FindPlace(Head: PNode; NewWord: string): PNode; var q: PNode; begin q := Head; while (q <> nil) and (NewWord > q^.word) do q := q^.next; Result := q; end; function FindPlace(Head: PNode; NewWord: string): PNode; var q: PNode; begin q := Head; while (q <> nil) and (NewWord > q^.word) do q := q^.next; Result := q; end; > слово NewWord стоїть за алфавітом перед q^.word слово NewWord стоїть за алфавітом перед q^.word
  • 47. 47 Видалення вузла procedure DeleteNode ( var Head: PNode; p: PNode ); var q: PNode; begin if Head = p then Head := p^.next else begin q := Head; while (q <> nil) and (q^.next <> p) do q := q^.next; if q <> nil then q^.next := p^.next; end; Dispose(p); end; procedure DeleteNode ( var Head: PNode; p: PNode ); var q: PNode; begin if Head = p then Head := p^.next else begin q := Head; while (q <> nil) and (q^.next <> p) do q := q^.next; if q <> nil then q^.next := p^.next; end; Dispose(p); end; while (q <> nil) and (q^.next <> p) do q := q^.next; if Head = p then Head := p^.next qq HeadHead pp nil Проблема: потрібно знати адресу попереднього вузла q. особливий випадок: видаляємо перший вузол особливий випадок: видаляємо перший вузол шукаємо вузол, такий що q^.next = p шукаємо вузол, такий що q^.next = p Dispose(p); звільнення пам'ятізвільнення пам'яті
  • 48. 48 Алфавітно-частотний словник Алгоритм: 1) відкрити файл для читання; 2) прочитати чергове слово (як?) 3) якщо файл закінчився, то перейти до кроку 7; 4) якщо слово знайдено, збільшити лічильник (поле count); 5) якщо слова немає в списку, то • створити новий вузол, заповнити поля (CreateNode); • знайти вузол, перед яким потрібно вставити слово (FindPlace); • добавити вузол (AddBefore); 1) перейти до кроку 2; 2) закрити файл 3) вивести список слів, використовуючи прохід по списку. var F: Text; ... Assign(F, 'input.dat'); Reset ( F ); var F: Text; ... Assign(F, 'input.dat'); Reset ( F ); Close ( F );Close ( F );
  • 49. 49 Як прочитати одне слово з файлу? function GetWord ( F: Text ) : string; var c: char; begin Result := ''; { порожній рядок } c := ' '; { пропуск – щоб увійти в цикл } { пропускаємо спецсимволи і пропуски } while not eof(f) and (c <= ' ') do read(F, c); { читаєм слово } while not eof(f) and (c > ' ') do begin Result := Result + c; read(F, c); end; end; function GetWord ( F: Text ) : string; var c: char; begin Result := ''; { порожній рядок } c := ' '; { пропуск – щоб увійти в цикл } { пропускаємо спецсимволи і пропуски } while not eof(f) and (c <= ' ') do read(F, c); { читаєм слово } while not eof(f) and (c > ' ') do begin Result := Result + c; read(F, c); end; end; Алгоритм: 1) пропускаємо всі спецсимволи і пропуски (з кодами <= 32) 2) читаємо всі символи до першого пропуску або спецсимволу Можна поміняти місцями рядки в циклі? Можна поміняти місцями рядки в циклі? ??
  • 50. 50 Повна програма type PNode = ^Node; Node = record ... end; { нові типи даних } var Head: PNode; { адреса голови списку } NewNode, q: PNode; { допоміжні вказівники } w: string; { слово з файлу } F: text; { файлова змінна } count: integer; { лічильник різних слів } { процедури і функції } begin Head := nil; Assign ( F, 'input.txt' ); Reset ( F ); { читаємо слова з файлу, будуємо список } Close ( F ); { виводимо список в інший файл } end. type PNode = ^Node; Node = record ... end; { нові типи даних } var Head: PNode; { адреса голови списку } NewNode, q: PNode; { допоміжні вказівники } w: string; { слово з файлу } F: text; { файлова змінна } count: integer; { лічильник різних слів } { процедури і функції } begin Head := nil; Assign ( F, 'input.txt' ); Reset ( F ); { читаємо слова з файлу, будуємо список } Close ( F ); { виводимо список в інший файл } end.
  • 51. 51 Повна програма (II) { читаємо слова з файлу, будуємо список } while True do begin { нескінченний цикл } w := GetWord ( F ); { читаємо слово } if w = '' then break; { слова закінчились, вихід } q := Find ( Head, w ); { шукаємо слово в списку } if q <> nil then { знайшли, збільшити лічильник } q^.count := q^.count + 1 else begin { не знайшли, добавити в список } NewNode := CreateNode ( w ); q := FindPlace ( Head, w ); AddBefore ( Head, q, NewNode ); end; end; { читаємо слова з файлу, будуємо список } while True do begin { нескінченний цикл } w := GetWord ( F ); { читаємо слово } if w = '' then break; { слова закінчились, вихід } q := Find ( Head, w ); { шукаємо слово в списку } if q <> nil then { знайшли, збільшити лічильник } q^.count := q^.count + 1 else begin { не знайшли, добавити в список } NewNode := CreateNode ( w ); q := FindPlace ( Head, w ); AddBefore ( Head, q, NewNode ); end; end;
  • 52. 52 Повна програма (III) { виводимо список в інший файл } q := Head; { прохід з початку списку } count := 0; { обнулили лічильник слів } Assign(F, 'output.txt'); Rewrite(F); while q <> nil do begin { поки не кінець списку } count := count + 1; { ще одне слово } writeln ( F, q^.word, ': ', q^.count ); q := q^.next; { перейти до наступного } end; writeln ( F, ‘Знайдено ', { виводимо список в інший файл } q := Head; { прохід з початку списку } count := 0; { обнулили лічильник слів } Assign(F, 'output.txt'); Rewrite(F); while q <> nil do begin { поки не кінець списку } count := count + 1; { ще одне слово } writeln ( F, q^.word, ': ', q^.count ); q := q^.next; { перейти до наступного } end; writeln ( F, ‘Знайдено ',
  • 53. type PNode = ^Node; { вказівник на вузол } Node = record { структура вузла } word: string[40]; { слово } count: integer; {лічильник повторений} next: PNode; {посилання на наступний} prev: PNode; {посилання на попередній} end; type PNode = ^Node; { вказівник на вузол } Node = record { структура вузла } word: string[40]; { слово } count: integer; {лічильник повторений} next: PNode; {посилання на наступний} prev: PNode; {посилання на попередній} end; 53 Двозв’язні списки Структура вузла: Адреси «голови» і «хвоста»: var Head, Tail: PNode; ... Head := nil; Tail := nil; var Head, Tail: PNode; ... Head := nil; Tail := nil; nextprev previous можна рухатися в обидва боки потрібно працювати з двома вказівниками замість одного nilnil HeadHead TailTail
  • 54. 54 Завдання «4»: «Зібрати» з цих функцій програму для побудови алфавітно-частотного словника. В кінці файлу вивести загальну кількість різних слів (кількість елементів списку). «5»: Те ж саме, але використовувати двозв’язні списки. «6»: Те ж саме, що і на «5», але вивести список слів в порядку зменшення частоти, тобто, спочатку ті слова, які зустрічаються найчастіше.
  • 55. Тема 5. Стеки, черги, деки Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 56. 56 Стек Стек – це лінійна структура даних, в якій додавання і видалення елементів можливо тільки з одного кінця (вершини стека). Stack = кипа, куча, стопка (англ.) LIFO = Last In – First Out «Хто останнім увійшов, той першим вийшов». Операції зі стеком: 1) додати елемент на вершину (Push = заштовхнути); 2) зняти елемент з вершини (Pop = вилетіти зі звуком).
  • 57. 57 Приклад завдання Завдання: вводиться символьний рядок, в якому записано вираз з дужками трьох типів: [], {} і (). Визначити, чи правильно розставлені дужки (не звертаючи уваги на інші символи). Приклади: [()]{} ][ [({)]} Спрощене завдання: те ж саме, але з одним видом дужок. Рішення: лічильник вкладеності дужок. Послідовність правильна, якщо в кінці лічильник дорівнює нулю і при проході ні разу не ставав негативним. Чи можна вирішити вихідне завдання так само, але з трьома лічильниками? Чи можна вирішити вихідне завдання так само, але з трьома лічильниками? ?? [ ( { ) ] } (: 0 1 0 [: 0 1 0 {: 0 1 0 [ ( { ) ] }[ ( { ) ] } ( ( ) ) ( ) 1 2 1 0 1 0 ( ( ) ) ( )( ( ) ) ( ) ( ( ) ) ) ( 1 2 1 0 -1 0 ( ( ) ) ) (( ( ) ) ) ( ( ( ) ) ( 1 2 1 0 1 ( ( ) ) (( ( ) ) (
  • 58. 58 Рішення завдання з дужками Алгоритм: 1) на початку стек порожній; 2) в циклі переглядаємо всі символи рядка по порядку; 3) якщо черговий символ – відкриваюча дужка, заносимо її на вершину стека; 4) якщо символ – закриваюча дужка, перевіряємо вершину стека: там повинна бути відповідна відкриваюча дужка (якщо це не так, то помилка); 5) якщо наприкінці стек не порожній, вираз неправильний. [ ( ( ) ) ] { }[ ( ( ) ) ] { } [ [ ( [ ( ( [ ( ( [ ( [ { {
  • 59. 59 Реалізація стека (масив) Структура-стек: const MAXSIZE = 100; type Stack = record { стек на 100 символів } data: array[1..MAXSIZE] of char; size: integer; { кількість елементів } end; const MAXSIZE = 100; type Stack = record { стек на 100 символів } data: array[1..MAXSIZE] of char; size: integer; { кількість елементів } end; Додавання елемента: procedure Push( var S: Stack; x: char); begin if S.size = MAXSIZE then Exit; S.size := S.size + 1; S.data[S.size] := x; end; procedure Push( var S: Stack; x: char); begin if S.size = MAXSIZE then Exit; S.size := S.size + 1; S.data[S.size] := x; end; помилка: переповнення стека помилка: переповнення стека добавити елементдобавити елемент Що погано?Що погано? ??
  • 60. 60 Реалізація стека (масив) function Pop ( var S:Stack ): char; begin if S.size = 0 then begin Result := char(255); Exit; end; Result := S.data[S.size]; S.size := S.size - 1; end; function Pop ( var S:Stack ): char; begin if S.size = 0 then begin Result := char(255); Exit; end; Result := S.data[S.size]; S.size := S.size - 1; end; Зняття елемента з вершини: Порожній чи ні? function isEmpty ( S: Stack ): Boolean; begin Result := (S.size = 0); end; function isEmpty ( S: Stack ): Boolean; begin Result := (S.size = 0); end; помилка: стек порожній помилка: стек порожній
  • 61. 61 Програма var br1, br2, expr: string; i, k: integer; upper: char; { то, що зняли зі стека } error: Boolean; { ознака помилки } S: Stack; begin br1 := '([{'; br2 := ')]}'; S.size := 0; error := False; writeln('Введіть вираз з дужками'); readln(expr); ... { тут буде основний цикл обробки } if not error and isEmpty(S) then writeln('Вираз правильний.') else writeln('Вираз неправильний.') end. var br1, br2, expr: string; i, k: integer; upper: char; { то, що зняли зі стека } error: Boolean; { ознака помилки } S: Stack; begin br1 := '([{'; br2 := ')]}'; S.size := 0; error := False; writeln('Введіть вираз з дужками'); readln(expr); ... { тут буде основний цикл обробки } if not error and isEmpty(S) then writeln('Вираз правильний.') else writeln('Вираз неправильний.') end. відкриваючі дужкивідкриваючі дужки закриваючі дужкизакриваючі дужки
  • 62. 62 Обробка рядка (основний цикл) for i:=1 to length(expr) do begin for k:=1 to 3 do begin if expr[i] = br1[k] then begin { відкр. дужка } Push(S, expr[i]); { заштовхнути в стек} break; end; if expr[i] = br2[k] then begin { закр. дужка } upper := Pop(S); { зняти символ із стека } error := upper <> br1[k]; break; end; end; if error then break; end; for i:=1 to length(expr) do begin for k:=1 to 3 do begin if expr[i] = br1[k] then begin { відкр. дужка } Push(S, expr[i]); { заштовхнути в стек} break; end; if expr[i] = br2[k] then begin { закр. дужка } upper := Pop(S); { зняти символ із стека } error := upper <> br1[k]; break; end; end; if error then break; end; цикл по всіх символах рядка expr цикл по всіх символах рядка expr цикл за всіма видами дужокцикл за всіма видами дужок помилка: стек порожній або не та дужка помилка: стек порожній або не та дужка була помилка: далі немає сенсу перевіряти була помилка: далі немає сенсу перевіряти
  • 63. 63 Реалізація стека (список) Додавання елемента: Структура вузла: type PNode = ^Node; { вказівник на вузол } Node = record data: char; { дані } next: PNode; { вказівник на наст. елемент } end; type PNode = ^Node; { вказівник на вузол } Node = record data: char; { дані } next: PNode; { вказівник на наст. елемент } end; procedure Push( var Head: PNode; x: char); var NewNode: PNode; begin New(NewNode); { виділити пам’ять } NewNode^.data := x; { записати символ } NewNode^.next := Head; { зробити першим вузлом } Head := NewNode; end; procedure Push( var Head: PNode; x: char); var NewNode: PNode; begin New(NewNode); { виділити пам’ять } NewNode^.data := x; { записати символ } NewNode^.next := Head; { зробити першим вузлом } Head := NewNode; end;
  • 64. 64 Реалізація стека (список) Зняття елемента з вершини: function Pop ( var Head: PNode ): char; var q: PNode; begin if Head = nil then begin { стек порожній } Result := char(255);{невикористовуваний символ} Exit; end; Result := Head^.data; { взяти верхній символ } q := Head; { запам’ятати вершину } Head := Head^.next; { видалити вершину з стека } Dispose(q); { видалити з пам’яті } end; function Pop ( var Head: PNode ): char; var q: PNode; begin if Head = nil then begin { стек порожній } Result := char(255);{невикористовуваний символ} Exit; end; Result := Head^.data; { взяти верхній символ } q := Head; { запам’ятати вершину } Head := Head^.next; { видалити вершину з стека } Dispose(q); { видалити з пам’яті } end; Чи можна переставляти оператори?Чи можна переставляти оператори? ?? q := Head; Head := Head^.next; Dispose(q); q := Head; Head := Head^.next; Dispose(q);
  • 65. 65 Реалізація стека (список) Зміни в основній програмі: var S: Stack; ... S.size := 0; var S: Stack; ... S.size := 0; var S: PNode;var S: PNode; S := nil;S := nil; Більше нічого не змінюється!Більше нічого не змінюється! !! Порожній чи ні? function isEmpty ( S: Stack ): Boolean; begin Result := (S = nil); end; function isEmpty ( S: Stack ): Boolean; begin Result := (S = nil); end;
  • 66. 66 Обчислення арифметичних виразів a b + c d + 1 - /a b + c d + 1 - / Як обчислювати автоматично: Інфіксний запис (знак операції між операндами) (a + b) / (c + d – 1)(a + b) / (c + d – 1) необхідні дужки! Постфіксний запис (знак операції після операндів) польська нотація, Jan Łukasiewicz (1920) польська нотація, Jan Łukasiewicz (1920) дужки не потрібні, можна однозначно обчислити! Префіксний запис (знак операції до операндів) / + a b - + c d 1/ + a b - + c d 1 зворотна польська нотація, F. L. Bauer and E. W. Dijkstra зворотна польська нотація, F. L. Bauer and E. W. Dijkstra a + ba + b a + ba + b c + dc + d c + dc + d c + d - 1c + d - 1 c + d - 1c + d - 1
  • 67. 67 Запишіть в постфіксній формі (32*6-5)*(2*3+4)/(3+7*2)(32*6-5)*(2*3+4)/(3+7*2) (2*4+3*5)*(2*3+18/3*2)*(12-3)(2*4+3*5)*(2*3+18/3*2)*(12-3) (4-2*3)*(3-12/3/4)*(24-3*12)(4-2*3)*(3-12/3/4)*(24-3*12)
  • 68. 68 Обчислення виразів Постфіксна форма: a b + c d + 1 - /a b + c d + 1 - / Алгоритм: 1) взяти черговий елемент; 2) якщо це не знак операції, добавить його в стек; 3) якщо це знак операції, то • взяти з стека два операнди; • виконати операцію і записати результат в стек; 1) перейти до кроку 1. a b a a+b c a+b d c a+b c+d a+b 1 c+d a+b c+d-1 a+b X X =X =
  • 69. 69 Системний стек (Windows – 1 Мб) Використовується для 1) розміщення локальних змінних; 2) зберігання адрес повернення (за якими переходить програма після виконання функції або процедури); 3) передачі параметрів в функції та процедури; 4) тимчасового зберігання даних (в програмах на мові Асемблер). Переповнення стека (stack overflow): 1) занадто багато локальних змінних (вихід – використовувати динамічні масиви); 2) дуже багато рекурсивних викликів функцій і процедур (вихід – переробити алгоритм так, щоб зменшити глибину рекурсії або відмовитися від неї взагалі).
  • 70. 70 Черга 1 2 3 4 5 6 Черга – це лінійна структура даних, в якій додавання елементів можливе тільки з одного кінця (кінця черги), а видалення елементів – тільки з іншого кінця (початку черги). FIFO = First In – First Out «Хто першим увійшов, той першим вийшов». Операції з чергою: 1) додати елемент в кінець черги (PushTail = заштовхнути в кінець); 2) видалити елемент з початку черги (Pop).
  • 71. 71 Реалізація черги (масив) 1 1 2 1 2 3 1 2 3 найпростіший спосіб 1) потрібно заздалегідь виділити масив; 2) при вибірці з черги потрібно зрушувати всі елементи.
  • 72. 72 Реалізація черги (кільцевий масив) 1 2 HeadHead TailTail 1 2 3 2 3 2 3 4 3 4 Скільки елементів можна зберігати в такій черзі? Скільки елементів можна зберігати в такій черзі? ?? Як розрізнити стани «черга порожня» і «черга повна»? Як розрізнити стани «черга порожня» і «черга повна»? ??3 45
  • 73. 73 Реалізація черги (кільцевий масив) 1 HeadHead TailTail В черзі 1 елемент: Черга порожня: Черга повна: Head = Tail + 1Head = Tail + 1 Head = (Tail mod N) + 1Head = (Tail mod N) + 1 1 N розмір масиву розмір масиву 1 23 Head = Tail + 2Head = Tail + 2 Head = (Tail+1) mod N + 1Head = (Tail+1) mod N + 1 1 N 1 2 3 Head = TailHead = Tail
  • 74. 74 Реалізація черги (кільцевий масив) type Queue = record data: array[1..MAXSIZE] of integer; head, tail: integer; end; type Queue = record data: array[1..MAXSIZE] of integer; head, tail: integer; end; Структура даних: Додавання в чергу: procedure PushTail( var Q: Queue; x: integer); begin if Q.head = (Q.tail+1) mod MAXSIZE + 1 then Exit; { черга повна, не додавати } Q.tail := Q.tail mod MAXSIZE + 1; Q.data[Q.tail] := x; end; procedure PushTail( var Q: Queue; x: integer); begin if Q.head = (Q.tail+1) mod MAXSIZE + 1 then Exit; { черга повна, не додавати } Q.tail := Q.tail mod MAXSIZE + 1; Q.data[Q.tail] := x; end; замкнути в кільце замкнути в кільцеmod MAXSIZE
  • 75. 75 Реалізація черги (кільцевий масив) Вибірка з черги: function Pop ( var S: Queue ): integer; begin if Q.head = Q.tail mod MAXSIZE + 1 then begin Result := MaxInt; Exit; end; Result := Q.data[Q.head]; Q.head := Q.head mod MAXSIZE + 1; end; function Pop ( var S: Queue ): integer; begin if Q.head = Q.tail mod MAXSIZE + 1 then begin Result := MaxInt; Exit; end; Result := Q.data[Q.head]; Q.head := Q.head mod MAXSIZE + 1; end; черга порожня черга порожня взяти перший елемент взяти перший елемент видалити його з черги видалити його з черги максимальне ціле число максимальне ціле число
  • 76. 76 Реалізація черги (списки) type PNode = ^Node; Node = record data: integer; next: PNode; end; type PNode = ^Node; Node = record data: integer; next: PNode; end; type Queue = record head, tail: PNode; end; type Queue = record head, tail: PNode; end; Структура вузла: Тип даних «черга»:
  • 77. 77 Реалізація черги (списки) procedure PushTail( var Q: Queue; x: integer ); var NewNode: PNode; begin New(NewNode); NewNode^.data := x; NewNode^.next := nil; if Q.tail <> nil then Q.tail^.next := NewNode; Q.tail := NewNode; { новий вузол – в кінець} if Q.head = nil then Q.head := Q.tail; end; procedure PushTail( var Q: Queue; x: integer ); var NewNode: PNode; begin New(NewNode); NewNode^.data := x; NewNode^.next := nil; if Q.tail <> nil then Q.tail^.next := NewNode; Q.tail := NewNode; { новий вузол – в кінець} if Q.head = nil then Q.head := Q.tail; end; Додавання елемента: створюємо новий вузол створюємо новий вузол якщо в списку вже щось було, додаємо в кінець якщо в списку вже щось було, додаємо в кінець якщо в списку нічого не було, … якщо в списку нічого не було, …
  • 78. 78 Реалізація черги (списки) function Pop ( var S: Queue ): integer; var top: PNode; begin if Q.head = nil then begin Result := MaxInt; Exit; end; top := Q.head; Result := top^.data; Q.head := top^.next; if Q.head = nil then Q.tail := nil; Dispose(top); end; function Pop ( var S: Queue ): integer; var top: PNode; begin if Q.head = nil then begin Result := MaxInt; Exit; end; top := Q.head; Result := top^.data; Q.head := top^.next; if Q.head = nil then Q.tail := nil; Dispose(top); end; Вибірка елемента: якщо список порожній, … якщо список порожній, … запам'ятали перший елемент запам'ятали перший елемент якщо в списку нічого не залишилося, … якщо в списку нічого не залишилося, … звільнити пам'ять звільнити пам'ять
  • 79. 79 Дека Дека (deque = double ended queue, черга з двома кінцями)– це лінійна структура даних, в якій додавання і видалення елементів можливе з обох кінців. 12 34 56 Операції з декою: 1) додавання елемента в початок (Push); 2) видалення елемента з початку (Pop); 3) додавання елемента в кінець (PushTail); 4) видалення елемента з кінця (PopTail). Реалізація: 1) кільцевий масив; 2) двохзв'язний список.
  • 80. 80 Завдання «4»: У файлі input.dat знаходиться список чисел (або слів). Переписати його в файл output.dat в зворотному порядку. «5»: Скласти програму, яка обчислює значення арифметичного виразу, записаного в постфіксній формі, за допомогою стека. Вираз правильний, допускаються тільки однозначні числа і знаки +, -, *, /. «6»: Те ж саме, що і в «5», але допускаються багатозначні числа.
  • 81. Тема 6. Дерева Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 82. 82 Дерева директордиректор гол. інженергол. інженер гол. бухгалтергол. бухгалтер інженерінженер інженерінженер інженерінженер бухгалтербухгалтер бухгалтербухгалтер бухгалтербухгалтер Що спільного в усіх прикладах? Що спільного в усіх прикладах? ??
  • 83. 83 Дерева Дерево – це структура даних, що складається з вузлів і з'єднують їх спрямовані ребра (дуги), причому в кожен вузол (крім кореневого) входить тільки одна дуга. Корінь – це початковий вузол дерева. Листок – це вузол, з якого не виходить жодної дуги. корінькорінь 22 88 55 66 99 11 33 44 1010 77 Які структури – не дерева? 44 11 3322 55 22 44 66 11 33 55 33 22 11 3322 11 44
  • 84. 84 Дерева Предок вузла x – це вузол, з якого існує шлях по стрілках у вузол x. Потомок вузла x – це вузол, в який існує шлях по стрілках з вузла x. Батько вузла x – це вузол, з якого існує дуга безпосередньо у вузол x. За допомогою дерев зображають відносини підпорядкованості (ієрархія, «старший – молодший», «батько – дитина»). За допомогою дерев зображають відносини підпорядкованості (ієрархія, «старший – молодший», «батько – дитина»). !! 22 44 66 11 33 55 Син вузла x – це вузол, в який існує дуга безпосередньо з вузла x. Брат вузла x (sibling) – це вузол, у якого той же батько, що й у вузла x. Висота дерева – це найбільша відстань від кореня до листка (кількість дуг).
  • 85. 85 Дерево – рекурсивна структура даних Рекурсивне визначення: 1. Порожня структура – це дерево. 2. Дерево – це корінь і кілька пов'язаних з ним дерев. 22 44 66 11 33 55 Двійкове (бінарне) дерево – це дерево, в якому кожен вузол має не більше двох синів. 1. Порожня структура – це двійкове дерево. 2. Двійкове дерево – це корінь і два пов'язаних з ним двійкових дерева (ліве і праве піддерева).
  • 86. 86 Двійкові дерева Структура вузла: type PNode = ^Node; { покажчик на вузол } Node = record data: integer; { корисні дані } left, right: PNode; { посилання на лівого і правого синів } end; type PNode = ^Node; { покажчик на вузол } Node = record data: integer; { корисні дані } left, right: PNode; { посилання на лівого і правого синів } end; Застосування: 1) пошук даних у спеціально побудованих деревах (бази даних); 2) сортування даних; 3) обчислення арифметичних виразів; 4) кодування (метод Хаффмана).
  • 87. 87 Двійкові дерева пошуку 1616 4545 3030 7676 125125 9898 5959 Яка закономірність?Яка закономірність? ?? Зліва від кожного вузла знаходяться вузли з меншими ключами, а справа – з більшими. Ключ – це характеристика вузла, по якій виконується пошук (найчастіше – одне з полів структури). Як шукати ключ, рівний x: 1) якщо дерево порожнє, ключ не знайдений; 2) якщо ключ вузла дорівнює x, то стоп. 3) якщо ключ вузла менше x, то шукати x в лівому піддереві; 4) якщо ключ вузла більше x, то шукати x в правом піддереві. Зведення задачі до такої ж задачі меншою розмірності – це …? Зведення задачі до такої ж задачі меншою розмірності – це …? ??
  • 88. 88 Двійкові дерева пошуку 1616 4545 3030 7676 125125 9898 5959 Пошук в масиві (N елементів): 1616454530307676 12512598985959 При кожному порівнянні відкидається 1 елемент. Кількість порівнянь – N. Пошук по дереву (N елементів): При кожному порівнянні відкидається половина залишившихся елементів. Кількість порівнянь ~ log2N. швидкий пошук 1) потрібно заздалегідь побудувати дерево; 2) бажано, щоб дерево було мінімальної висоти.
  • 89. 89 Реалізація алгоритму пошуку { Функція Search – пошук по дереву Вхід: Tree - адреса кореня, x - що шукаємо Вихід: адреса вузла або nil (не знайшли) } function Search(Tree: PNode; x: integer): PNode; begin if Tree = nil then begin Result := nil; Exit; end; if x = Tree^.data then Result := Tree else if x < Tree^.data then Result := Search(Tree^.left, x) else Result := Search(Tree^.right, x); end; { Функція Search – пошук по дереву Вхід: Tree - адреса кореня, x - що шукаємо Вихід: адреса вузла або nil (не знайшли) } function Search(Tree: PNode; x: integer): PNode; begin if Tree = nil then begin Result := nil; Exit; end; if x = Tree^.data then Result := Tree else if x < Tree^.data then Result := Search(Tree^.left, x) else Result := Search(Tree^.right, x); end; дерево порожнє: ключ не знайшли… дерево порожнє: ключ не знайшли… знайшли, повертаємо адресу кореня знайшли, повертаємо адресу кореня шукати в лівому піддереві шукати в лівому піддереві шукати в правому піддеревішукати в правому піддереві
  • 90. 90 Як побудувати дерево пошуку? { Процедура AddToTree – додати елемент Вхід: Tree - адреса кореня, x - що додаємо } procedure AddToTree( var Tree: PNode; x: integer ); begin if Tree = nil then begin New(Tree); Tree^.data := x; Tree^.left := nil; Tree^.right := nil; Exit; end; if x < Tree^.data then AddToTree(Tree^.left, x) else AddToTree(Tree^.right, x); end; { Процедура AddToTree – додати елемент Вхід: Tree - адреса кореня, x - що додаємо } procedure AddToTree( var Tree: PNode; x: integer ); begin if Tree = nil then begin New(Tree); Tree^.data := x; Tree^.left := nil; Tree^.right := nil; Exit; end; if x < Tree^.data then AddToTree(Tree^.left, x) else AddToTree(Tree^.right, x); end; дерево порожнє: створюємо новий вузол (корінь) дерево порожнє: створюємо новий вузол (корінь) адреса кореня може змінюватися адреса кореня може змінюватися додаємо до лівого або правого піддерево додаємо до лівого або правого піддерево Мінімальна висота не гарантується!Мінімальна висота не гарантується! !!
  • 91. 91 Обхід дерева 1616 4545 3030 7676 125125 9898 5959Обхід дерева – це перерахування всіх вузлів в певному порядку. Обхід ЛКП («лівий – корінь – правий»): 125125989876764545 595930301616 Обхід ПКЛ («правий – корінь – лівий»): 1616303045457676 59599898125125 Обхід КЛП («корінь – лівий – правий»): 125125767698981616 454530305959 Обхід ЛПК («лівий – правий – корінь»): 595998981251253030 767645451616
  • 92. 92 Обхід дерева – реалізація { Процедура LKP – обхід дерева в порядку ЛКП (лівий – корінь – правий) Вхід: Tree - адреса кореня } procedure LKP(Tree: PNode); begin if Tree = nil then Exit; LKP(Tree^.left); write(' ', Tree^.data); LKP(Tree^.right); end; { Процедура LKP – обхід дерева в порядку ЛКП (лівий – корінь – правий) Вхід: Tree - адреса кореня } procedure LKP(Tree: PNode); begin if Tree = nil then Exit; LKP(Tree^.left); write(' ', Tree^.data); LKP(Tree^.right); end; обхід цієї гілки закінчено обхід цієї гілки закінчено обхід лівого піддерева обхід лівого піддерева виведення даних кореня виведення даних кореня обхід правого піддерева обхід правого піддерева Для рекурсивної структури зручно застосовувати рекурсивну обробку! Для рекурсивної структури зручно застосовувати рекурсивну обробку! !!
  • 93. 93 Розбір арифметичних виразів aa bb ++ ++ 11 -- // cc dd a b + c d + 1 - /a b + c d + 1 - / Як обчислювати автоматично: Інфіксний запис, обхід ЛКП (знак операції між операндами) (a + b) / (c + d – 1)(a + b) / (c + d – 1) необхідні дужки! Постфіксний запис, ЛПК (знак операції після операндів) a + b / c + d – 1a + b / c + d – 1 польська нотація, Jan Łukasiewicz (1920) польська нотація, Jan Łukasiewicz (1920) дужки не потрібні, можна однозначно обчислити! Префіксний запис, КЛП (знак операції до операндів) / + a b - + c d 1/ + a b - + c d 1 зворотна польська нотація, F. L. Bauer and E. W. Dijkstra зворотна польська нотація, F. L. Bauer and E. W. Dijkstra
  • 94. 94 Обчислення виразів Постфіксна форма: a b + c d + 1 - /a b + c d + 1 - / Алгоритм: 1) взяти черговий елемент; 2) якщо це не знак операції, додати його в стек; 3) якщо це знак операції, то • взяти з стека два операнди; • виконати операцію і записати результат у стек; 1) перейти до кроку 1. a b a a+b c a+b d c a+b c+d a+b 1 c+d a+b c+d-1 a+b X X =X =
  • 95. 95 Обчислення виразів Завдання: в символьному рядку записаний правильний арифметичний вираз, який може містити лише однозначні числа і знаки операцій +-*. Обчислити цей вираз. Алгоритм: 1) ввести рядок; 2) побудувати дерево; 3) обчислити вираз по дереву. Обмеження: 1) помилки не обробляються; 2) багатозначні числа не дозволені; 3) дробові числа не дозволені; 4) дужки не дозволені.
  • 96. 96 Побудова дерева Алгоритм: 1) якщо first=last (залишився один символ – число), то створити новій вузол і записати в нього цей елемент; інакше... 2) серед елементів віт first до last включно знайти останню операцію (елемент з номером k); 3) створити новий вузол (корінь) і записати в нього знак операції; 4) рекурсивно застосувати цей алгоритм два рази: • побудувати ліве піддерево, розібравши вираз з елементів масиву з номерами від first до k-1; • побудувати праве піддерево, розібравши вираз з елементів масиву з номерами від k+1 до last. 5 + 7 * 6 - 3 * 2 firstfirst lastlastkk k+1k+1k-1k-1
  • 97. 97 Як знайти останню операцію? Порядок виконання операцій • множення і ділення; • додавання і віднімання. 5 + 7 * 6 - 3 * 2 Потрібно шукати останню операцію з найменшим пріоритетом! Потрібно шукати останню операцію з найменшим пріоритетом! !! Пріоритет (старшинство) – число, що визначає послідовність виконання операцій: раніше виконуються операції з великим пріоритетом: • множення і ділення (пріоритет 2); • додавання і віднімання (пріоритет 1).
  • 98. 98 Пріоритет операції { Функція Priority – пріоритет операції Вхід: символ операції Вихід: пріоритет або 100, якщо не операція } function Priority ( c: char ): integer; begin case ( c ) of '+', '-': Result := 1; '*', '/': Result := 2; else Result := 100; end; end; { Функція Priority – пріоритет операції Вхід: символ операції Вихід: пріоритет або 100, якщо не операція } function Priority ( c: char ): integer; begin case ( c ) of '+', '-': Result := 1; '*', '/': Result := 2; else Result := 100; end; end; додавання і віднімання: пріоритет 1 додавання і віднімання: пріоритет 1 множення і ділення: пріоритет 2 множення і ділення: пріоритет 2це взагалі не операція це взагалі не операція
  • 99. 99 Номер останньої операції { Функція LastOperation – номер останньої операції Вхід: рядок, номери першого і останнього символів розглянутої частини Вихід: номер символу - останньої операції } function LastOperation ( Expr: string; first, last: integer): integer; var MinPrt, i, k, prt: integer; begin MinPrt := 100; for i:=first to last do begin prt := Priority ( Expr[i] ); if prt <= MinPrt then begin MinPrt := prt; k := i; end; end; Result := k; end; { Функція LastOperation – номер останньої операції Вхід: рядок, номери першого і останнього символів розглянутої частини Вихід: номер символу - останньої операції } function LastOperation ( Expr: string; first, last: integer): integer; var MinPrt, i, k, prt: integer; begin MinPrt := 100; for i:=first to last do begin prt := Priority ( Expr[i] ); if prt <= MinPrt then begin MinPrt := prt; k := i; end; end; Result := k; end; перевіряємо всі символи перевіряємо всі символи повернути номер символу повернути номер символу знайшли операцію з мінімальним пріоритетом знайшли операцію з мінімальним пріоритетом
  • 100. 100 Побудова дерева Структура вузла type PNode = ^Node; Node = record data: char; left, right: PNode; end; type PNode = ^Node; Node = record data: char; left, right: PNode; end; Створення вузла для числа (без нащадків) function NumberNode(c: char): PNode; begin New(Result); Result^.data := c; Result^.left := nil; Result^.right := nil; end; function NumberNode(c: char): PNode; begin New(Result); Result^.data := c; Result^.left := nil; Result^.right := nil; end; повертає адресу створено вузла повертає адресу створено вузла один символ, число один символ, число
  • 101. 101 Побудова дерева { Функція MakeTree – побудова дерева Вхід: рядок, номери першого і останнього символів розглянутої частини Вихід: адреса побудованого дерева } function MakeTree ( Expr: string; first, last: integer): PNode; var k: integer; begin if first = last then begin Result := NumberNode ( Expr[first] ); Exit; end; k := LastOperation ( Expr, first, last ); New(Result); Result^.data := Expr[k]; Result^.left := MakeTree ( Expr, first, k-1 ); Result^.right := MakeTree ( Expr, k+1, last ); end; { Функція MakeTree – побудова дерева Вхід: рядок, номери першого і останнього символів розглянутої частини Вихід: адреса побудованого дерева } function MakeTree ( Expr: string; first, last: integer): PNode; var k: integer; begin if first = last then begin Result := NumberNode ( Expr[first] ); Exit; end; k := LastOperation ( Expr, first, last ); New(Result); Result^.data := Expr[k]; Result^.left := MakeTree ( Expr, first, k-1 ); Result^.right := MakeTree ( Expr, k+1, last ); end; залишилося тільки число залишилося тільки число новий вузол: операціяновий вузол: операція
  • 102. 102 Обчислення виразів по дереву { Функція CalcTree – обчислення по дереву Вхід: адреса дерева Вихід: значення виразу } function CalcTree(Tree: PNode): integer; var num1, num2: integer; begin if Tree^.left = nil then begin Result := Ord(Tree^.data) - Ord('0'); Exit; end; num1 := CalcTree(Tree^.left); num2 := CalcTree(Tree^.right); case Tree^.data of '+': Result := num1+num2; '-': Result := num1-num2; '*': Result := num1*num2; '/': Result := num1 div num2; else Result := MaxInt; end; end; { Функція CalcTree – обчислення по дереву Вхід: адреса дерева Вихід: значення виразу } function CalcTree(Tree: PNode): integer; var num1, num2: integer; begin if Tree^.left = nil then begin Result := Ord(Tree^.data) - Ord('0'); Exit; end; num1 := CalcTree(Tree^.left); num2 := CalcTree(Tree^.right); case Tree^.data of '+': Result := num1+num2; '-': Result := num1-num2; '*': Result := num1*num2; '/': Result := num1 div num2; else Result := MaxInt; end; end; повернути число, якщо це лист повернути число, якщо це лист обчислюємо операнди (піддерева) обчислюємо операнди (піддерева) виконуємо операцію виконуємо операцію некоректна операція некоректна операція
  • 103. 103 Основна програма { Введення і обчислення виразу за допомою дерева } program qq; var Tree: PNode; Expr: string; { процедури і функції } begin write('Введіть вираз > '); readln( Expr ); Tree := MakeTree( Expr, 1, Length(Expr) ); writeln(' = ', CalcTree(Tree) ); end. { Введення і обчислення виразу за допомою дерева } program qq; var Tree: PNode; Expr: string; { процедури і функції } begin write('Введіть вираз > '); readln( Expr ); Tree := MakeTree( Expr, 1, Length(Expr) ); writeln(' = ', CalcTree(Tree) ); end.
  • 104. 104 Дерево ігри Завдання. Перед двома гравцями лежать дві купки каміння, у першій з яких 3, а в другій – 2 камені. У кожного гравця необмежено багато каменів. Гравці ходять по черзі. Хід полягає в том, що гравець або збільшує в 3 рази кількість каменів в якійсь купі, або додає 1 камінь в якусь купу. Виграє гравець, після ходу якого загальна кількість каменів у двох купах стає не менше 16. Хто виграє при безпомилковій грі – гравець, що робить перший хід, чи гравець, який робить другий хід? Як повинен ходити гравець що виграв?
  • 105. 105 Дерево ігри 3, 23, 2 гравецьгравець 11 3, 63, 6 27, 227, 227, 227, 2 3, 183, 183, 183, 18 3, 33, 3 4, 24, 2 12, 212, 2 4, 64, 6 5, 25, 2 4, 34, 3 9, 39, 3 4, 34, 3 3636,, 223636,, 22 44,, 181844,, 1818 1515,, 221515,, 22 12, 212, 2 4, 64, 6 5, 35, 3 4, 44, 4 3636, 2, 23636, 2, 2 1212,, 661212,, 66 1515,, 331515,, 33 1212,, 441212,, 44 27, 327, 327, 327, 3 При правильній грі виграє гравець 2гравець 2!При правильній грі виграє гравець 2гравець 2! !! гравецьгравець 11гравець 2гравець 2 гравецьгравець 22 9, 29, 2 4, 34, 3 4, 34, 3 ключовий хід ключовий хід виграв гравець 1гравець 1 виграв гравець 1гравець 1
  • 106. 106 Завдання «4»: «Зібрати» програму для обчислення правильного арифметичного виразу, що включає тільки однозначні числа і знаки операцій +, -, * , /. «5»: Те ж саме, але допускаються також багатозначні числа і дужки. «6»: Те ж саме, що і в «5», але з обробкою помилок (повинно виводитися повідомлення).
  • 107. Тема 7. Графи Динамічні структури даних (мова Паскаль) © К.Ю. Поляков, 2008-2010 Переклад: Р. М. Васильчик
  • 108. 108 Визначення Граф – це набір вершин (вузлів) і з'єднуючих їх ребер (дуг). Спрямований граф (орієнтований, орграф) – це граф, в якому всі дуги мають напрями. Ланцюг – це послідовність ребер, що з'єднують дві вершини (в орграфі – шлях). Цикл – це ланцюг з якоїсь вершини в неї саму. Зважений граф (мережа) – це граф, в якому кожному ребру приписується вага (довжина). 5533 22 44 11 33 22 44 11 Дерево – це граф?Дерево – це граф? ?? Так, без циклів!Так, без циклів!
  • 109. 109 Визначення Зв'язний граф – це граф, в якому існує ланцюг між кожною парою вершин. k-зв'язний граф – це граф, який можна розбити на k зв'язних частин. Повний граф – це граф, в якому проведено всі можливі ребра (n вершин → n(n-1)/2 ребер). 5533 22 44 11 88 66 77 22 11 33 22 11 33 44
  • 110. 110 Опис графа Матриця суміжності – це матриця, елемент M[i][j] якої дорівнює 1, якщо існує ребро з вершини i в вершину j, і дорівнює 0, якщо такого ребра немає. 5533 22 44 11 0 1 1 1 0 1 0 0 1 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 2 3 4 5 1 2 3 4 5 5533 22 44 11 0 1 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 2 3 4 5 1 2 3 4 5 Симетрія!Симетрія! !! Список суміжності 2 3 4 1 4 5 1 1 2 5 2 4 1 2 3 4 5 2 3 4 4 5 5 1 2 3 4 5
  • 111. 111 Матриця і список суміжності 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 2211 5533 44 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 11 5533 44 22
  • 112. 112 Побудова графа по матриці суміжності 0 0 1 0 0 0 0 1 0 1 1 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 2 3 4 5 1 2 3 4 5 0 0 1 1 1 0 1 0 1 0 0 1 0 1 0 1 1 0 0 0 0 1 1 0 0 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 11 33 55 22 44 11 33 55 22 44
  • 113. 113 Як виявити ланцюги і цикли? Завдання: визначити, чи існує ланцюг довжини k з вершини i в вершину j (або цикл довжиною k з вершини i в неї саму). M2 [i][j]=1, если M[i][1]=1 і M[1][j]=1 або M[i][2]=1 і M[2][j]=1 або M[i][3]=1 і M[3][j]=1 рядок iрядок i логічне множення логічне множення стовпець jстовпець j логічне додавання логічне додавання 11 33 44 22 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 1 2 3 4 1 2 3 4 M = або M[i][4]=1 і M[4][j]=1
  • 114. 114 Як виявити ланцюги і цикли? M2 = M ⊗ MM2 = M ⊗ M Логічне множення матриці на себе: матриця шляхів довжини 2 матриця шляхів довжини 2 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 M2 = ⊗ 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 = 0 1 0 1 0 0 1 0 11 0 0 0 0 0 1 0 11 33 44 22 1 2 3 4 1 2 3 4 M2 [3][1] = 0·0 + 1·1 + 0·0 + 1·1 = 1 маршрут 3-2-1маршрут 3-2-1 маршрут 3-4-1маршрут 3-4-1
  • 115. 115 Як виявити ланцюги і цикли? M3 = M2 ⊗ MM3 = M2 ⊗ M Матриця шляхів довжини 3: 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 M3 = ⊗ = 11 0 0 0 0 11 0 1 0 0 11 0 0 1 0 11 11 33 44 22 1 2 3 4 1 2 3 4 0 1 0 1 0 0 1 0 1 0 0 0 0 0 1 0 на головній діагоналі – цикли! на головній діагоналі – цикли! 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 M4 = ⊗ = 00 00 11 00 11 00 00 00 00 11 00 11 11 00 00 00 1 2 3 4 1 2 3 4 1 0 0 0 0 1 0 1 0 0 1 0 0 1 0 1
  • 116. 116 Вагова матриця 5533 22 44 11 3 5 7 4 6 8 5533 22 44 11 3 5 7 4 6 8 Вагова матриця – це матриця, елемент W[i,j] якої дорівнює вазі ребра з вершини i в вершину j (якщо воно є), або дорівнює ∞, якщо такого ребра немає. 0 7 3 5 ∞ 7 0 ∞ 4 8 3 ∞ 0 ∞ ∞ 5 4 ∞ 0 6 ∞ 8 ∞ 6 0 1 2 3 4 5 1 2 3 4 5 0 7 3 5 ∞ ∞ 0 ∞ 4 8 3 ∞ 0 ∞ ∞ 5 ∞ ∞ 0 6 ∞ 8 ∞ ∞ 0 1 2 3 4 5 1 2 3 4 5
  • 117. 117 Задача Прима-Краскала Завдання: з'єднати N міст телефонною мережею так, щоб довжина телефонних ліній була мінімальною. Те ж завдання: дано зв'язний граф з N вершинами, веги ребер задані ваговою матрицею W. Потрібно знайти набір ребер, що з'єднує всі вершини графа (остовне дерево) і має найменшу вагу. 5533 22 44 11 3 5 7 4 6 8 0 7 3 5 ∞ 7 0 ∞ 4 8 3 ∞ 0 ∞ ∞ 5 4 ∞ 0 6 ∞ 8 ∞ 6 0 1 2 3 4 5 1 2 3 4 5
  • 118. 118 Жадібний алгоритм Жадібний алгоритм – це багатокроковий алгоритм, в якому на кожному кроці приймається рішення, краще в даний момент. В цілому може вийти не оптимальне рішення (послідовність кроків)! В цілому може вийти не оптимальне рішення (послідовність кроків)! !! Крок в задачі Прима-Краскала – це вибір ще невибраного ребра і додавання його до рішення. 5533 22 44 11 3 5 7 4 6 8 В задачі Прима-Краскала жадібний алгоритм дає оптимальне рішення! В задачі Прима-Краскала жадібний алгоритм дає оптимальне рішення! !!
  • 119. 119 Реалізація алгоритму Прима-Краскала Проблема: як перевірити, що 1) ребро не вибрано, і 2) ребро не утворює циклу з вибраними ребрами. Рішення: присвоїти кожній вершині свій колір і перефарбовувати вершини при додаванні ребра. 5533 22 44 1111 3 5 7 4 6 8 3333 2222 44 5555 Алгоритм: 1) пофарбувати всі вершини в різні кольори; 2) зробити N-1 раз в циклі:  вибрати ребро (i,j) мінімальної довжини з усіх ребер, що з'єднують вершини різного кольору;  перефарбувати всі вершини, що мають колір j, в колір i. 1) вивести знайдені ребра. 4444
  • 120. 120 Реалізація алгоритму Прима-Краскала Структура «ребро»: type rebro = record i, j: integer; { номери вершин } end; type rebro = record i, j: integer; { номери вершин } end; const N = 5; var W: array[1..N,1..N] of integer; Color: array[1..N] of integer; i, j, k, min, col_i, col_j: integer; Reb: array[1..N-1] of rebro; begin ... { тут треба ввести матрицю W } for i:=1 to N do { розфарбувати в різні кольори } Color[i] := i; ... { основний алгоритм – заповнення масиву Reb } ... { вивести знайдені ребра (масив Reb)} end. const N = 5; var W: array[1..N,1..N] of integer; Color: array[1..N] of integer; i, j, k, min, col_i, col_j: integer; Reb: array[1..N-1] of rebro; begin ... { тут треба ввести матрицю W } for i:=1 to N do { розфарбувати в різні кольори } Color[i] := i; ... { основний алгоритм – заповнення масиву Reb } ... { вивести знайдені ребра (масив Reb)} end. Основна програма: вагова матриця вагова матриця колір верши н колір верши н
  • 121. 121 Реалізація алгоритму Прима-Краскала for k:=1 to N-1 do begin min := MaxInt; for i:=1 to N do for j:=i+1 to N do if (Color[i] <> Color[j]) and (W[i,j] < min) then begin min := W[i,j]; Reb[k].i := i; Reb[k].j := j; col_i := Color[i]; col_j := Color[j]; end; for i:=1 to N do if Color[i] = col_j then Color[i] := col_i; end; for k:=1 to N-1 do begin min := MaxInt; for i:=1 to N do for j:=i+1 to N do if (Color[i] <> Color[j]) and (W[i,j] < min) then begin min := W[i,j]; Reb[k].i := i; Reb[k].j := j; col_i := Color[i]; col_j := Color[j]; end; for i:=1 to N do if Color[i] = col_j then Color[i] := col_i; end; Основний алгоритм: потрібно вибрати всього N-1 ребро потрібно вибрати всього N-1 ребро цикл по всіх парах вершин цикл по всіх парах вершин враховуємо тільки пари з різним кольором вершин враховуємо тільки пари з різним кольором вершин запам'ятовуємо ребра і кольори вершин запам'ятовуємо ребра і кольори вершин перефарбовуєм вершини кольору col_j перефарбовуєм вершини кольору col_j
  • 122. 122 Складність алгоритму Основний цикл: O(N3 )O(N3 ) for k:=1 to N-1 do begin ... for i:=1 to N do for j:=i+1 to N do ... end; for k:=1 to N-1 do begin ... for i:=1 to N do for j:=i+1 to N do ... end; три вкладених цикли, в кожному кількість кроків <=N три вкладених цикли, в кожному кількість кроків <=N зростає не швидше, ніж N3 Необхідна пам'ять: var W: array[1..N,1..N] of integer; Color: array[1..N] of integer; Reb: array[1..N-1] of rebro; var W: array[1..N,1..N] of integer; Color: array[1..N] of integer; Reb: array[1..N-1] of rebro; O(N2 )O(N2 ) Кількість операцій: