
- •1. Сортування
- •1.1. Сортування масивів
- •1. Сортування простими включеннями;
- •2. Сортування простим вибором;
- •1.1.1. Сортування простими включеннями
- •1.1.2. Сортування простим вибором
- •1.1.3. Сортування простим обміном (засіб “бульбашки”)
- •1.1.4. Шейкер-сортування
- •1.1.5. Швидке сортування
- •1.2. Сортування масива рядків
- •1.3. Сортування файлів
- •2. Рекурсивні алгоритми
- •2.1. Алгоритми з поверненням
- •2.1.1. Шахова задача про хід коня
- •2.1.2. Шахова задача про вісім ферзів
- •3. Динамічні інформаційні структури
- •3.1. Динамічні змінні. Вказівники
- •3.1.1. Засоби створення та використання динамічних даних
- •3.2. Рекурсивні типи даних
- •3.3. Списки
- •3.3.2. Двозв’язні та кiльцевi списки
- •3.3.3. Черги і стеки
- •3.4. Деревовидні структури
- •3.4.1. Бінарні дерева
- •3.4.2. Ідеально збалансовані дерева
- •3.4.3. Дерева пошуку
- •3.4.4. Збалансовані дерева (авл-дерева)
- •4. Завдання до лабораторних та контрольних робіт.
- •4.1. Сортування.
- •4.2. Списки.
3.2. Рекурсивні типи даних
Існують аналогії проміж методами структурування алгоритмів та методами структурування даних.
Елементарним неструктурованим оператором являється оператор присвоювання. Відповідний йому тип даних – простий, скалярний, неструктурований (тип вважається скалярним, якщо всі його значення впорядковані – це всі стандартні, а також перелічимий).
Оператор присвоювання та прості типи даних являються атомарними будівельними блоками для складних типів даних.
Аналог складеного (Begin ... End) – запис, обидві структури складаються з кінцевої кількості компонент.
Щоб описати повторення, число яких відоме і кінцеве, використовують цикл з параметром For; аналог – структура масив. Вибір з двох або більше варіантів виконується умовним оператором вибору; аналог структури даних – запис з варіантами.
Циклам While i Repeat, які допускають потенціально нескінченне число повторень, відповідає структура даних – файл.
Аналогом рекурсивної процедури являються рекурсивні типи даних. По аналогії з рекурсивною процедурою, яка містить у собі виклики самої себе, значення рекурсивного типу даних повинні мати одну або більше компонент того ж типу, що і все значення. Приклад – генеалогічне дерево. Iм’я людини – імена двох батьків, кожен з яких – людина, що має двох батьків і т. д.
Характерна особливість динамічних структур – їх можливість змінювати розмір. Тому пам’ять під них розподіляється динамічно, тобто в момент появи окремих компонент. Окремі компоненти пов’язані між собою вказівникми. Приклади простих рекурсивних структур – лінійні списки, стеки, черги, дерева. На практиці зустрічаються більш складні структури.
Приклад динамічної структури – черга на прийом до лікаря. Кожний пацієнт запам’ятовує людину, за якою зайняв чергу. Всі пацієнти зв’язані у ланцюжок відповідно з чергою, але у просторі вони разміщені довільним чином, хто прийшов - сідає на будь-яке вільне місце. Якщо хтось із пацієнтів покидає чергу, немає необхідності іншим пацієнтам переміщатись, просто той, хто за ним стояв, запам’ятовує тепер другу людину, за якою він зостався в черзі.
Аналогічно будується структура зв’язаних даних, які можуть разміщуватись у пам’яті в будь-якому вільному місці, не підряд, але мають вказівнику на попередній єлемент ланцюжка.
3.3. Списки
3.3.1. Лінійні однозв’язні списки
Лінійний список – це послiдовнiсть однотипних елементів, що розташовані лінійно один за одним та пов’язаних вказівникми.
Кожна змінна списку являє собою запис, оскільки складається мінімум з двох компонент: ідентифікуючого ключа (значення) та вказівника на попередній елемент.
Список може бути порожнім.
Опис типу елемента списку може мати вигляд:
Type
list = ^elem;
elem = Record
key : integer;
next : list;
.......... {інша можлива інформація}
End;
Var p,q : list;
………………………………..
Правила мови тільки при описі вказівників допускають використання імені ( elem ) до його опису.
Приклад списку у схематичному виглядi:
р
Для списку визначені поняття заголовного, кінцевого та поточного елементу, а також наступного та попереднього по відношенню до поточного. Поточний елемент – це елемент, який у даний час доступний для обробки.
Базисними операціями для списку є:
– створення порожнього списку;
– перевірка на порожність;
– перевірка існування попереднього та наступного елементів;
– призначення поточним першого, останнього, попереднього, наступного елементів;
– заміна поточного елемента із списку на деяке значення базисного типу;
– вилучення поточного елементу із списку;
– включення елементу базисного типу у список перед поточним елементом або після нього.
Список, кожен елемент якого зсилається на попереднiй, називається однозв’язним.
Для того, щоб у списку змінити послiдовнiсть записів, достатньо поміняти місцями лише значення вказівників, самi ж елементи списку залишаються на своїх мiсцях. З цієї точки зору над елементами списку зручно робити потужні операції, пов’язані з переставленням, наприклад, сортування .
Побудова списку
Починаючи з порожнього списку, включаючи елементи у початок списку, можна побудувати список з n елементів наступним чином:
{Побудова списку з n елементів.}
{q – допоміжна змінна зсилочного типу}
{n – ідентифікуючий ключ 4,3,2,1}
{p – вказівник на список }
Type
list = ^elem;
elem = Record
key : integer;
next : list;
End;
Var
p,q : list;
n : integer;
Begin
n:=4;
p:=Nil; {створити порожній список}
While n>0 Do
Begin
New(q);
q^.key :=n;
q^.next:=p;
p:=q;
n:=n-1
End;
Writeln(‘Список створено ‘);
End.
Процес створення списку можна представити схематично наступним чином:
– на першому кроці при P=Nil маємо порожній список:
-
при n=4 :
New(q); q^.key:=n; q^.next:=p; p:=q;
– при n=3 (поетапно):
New(q);
q^.key:=n;
q^.next:=p;
q
p:=q;
p
тобто p – тепер вказівник на новий елемент списку, який є заголовним елементом списку, і т.д.
Для пошуку елемента у списку потрібний прохід по списку від початку до кінця. Якщо необхідно мати доступ до кінця списку, можна в кожному елементі зберігати адресу останнього елементу, або окремо зберігати її у допоміжному елементі.
Елементарні дії із списками – включення та вилучення елементів, перегляд списку (або прохід по списку).
Прохід по списку
Основна операція із списком – прохід по списку (з метою виконання будь-якої операції з кожним елементом списку).
Описується наступним чином:
While p<>Nil Do
Begin
p(p^);
p:=p^.next
End;
Тут p(p^) – будь – які дії з кожним елементом списку.
(Поки вказівник на наступний елемент не дорівнює Nil , виконати потрібні дії з елементом списку, перейти до наступного елементу).
Включення у список після елемента, вказівник на який відомий.
Елемент, на який вказує вказівник q, необхідно включити у список після елемента, на який вказує вказівник p.
q
Елемент q повинен посилатися на той, на який посилається p, тобто:
q^.next:=p^.next;
Елемент p повинен посилатися на q, тобто:
p^.next:=q;
Включення
у список перед елементом, вказівник p^
на який відомий.
7
*7
Щоб включити новий елемент перед елементом з ключем 5, необхідно знати вказівник на нього, але він невідомий. Можна включити елемент після нього, а потім як би поміняти їх місцями.
New(q);
q^:=p^; {усі поля змінної q отримають значення змінної p}
p^.key:=8;
p^.next:=q; {поля змінної p отримають значення змінної q}
Вилучення із списку
Вилучення із списку здійснюється аналогічно включенню.
Якщо p – заголовний елемент списку, то
p^.next – адреса другого елементу списку,
(p^.next)^.next – адреса третього елементу списку і т.д.
Наприклад– вилучити із списку перший елемент:
q:=p;
p:=p^.next ;
Dispose(q);
Пошук у списку за заданим ключем x
..........................
b:=true;
While (p<>Nil) and b Do
If p^.key=x Then b:=false
Else
p:=p^.next;
..........................
If Notb Then <є такий елемент>
Else <такого елемента немає>;
{p=Nil or Notb} – умова виходу з циклу.
Задача. Складання частотного словника
Після зчитування тексту визначається список різних вхідних у нього слів та вказується частота їх появи у тексті.
Опис алгоритму:
– кожне чергове слово, що прочитане у тексті, шукається у списку;
– якщо таке слово є у списку, тобто вже зустрічалося у тексті, лічильник частоти його появи збільшується на одиницю;
– якщо немає слова у списку, воно додається у нього.
{Пошук з включенням у список}
Uses Crt;
Type
ref = ^Wword;
Wword = Record
key : integer;
count : integer;
next : ref
End;
Var
k : integer;
root : ref;
Procedure Search (x:integer; Var root:ref);
Var
w : ref;
b : boolean;
Begin
w:=root;
b:=true;
While (w<>Nil) and b Do
If w^.key=x Then b:=false
Else w:=w^.next;
If b Then
Begin
{новий елемент}
w:=root;
New(root); {формується адреса нової вершини}
With root^ Do
Begin
key:=x;
count:=1;
next:=w
End
End
Else
w^.count:=w^.count+1
End; {Search}
{------------------------------------}
Procedure Printlist (w:ref);
Begin
While w<>Nil Do
Begin
Writeln (w^.key,’ ’, w^.count);
w:=w^.next
End
End; {Printlist}
{------------------------------------}
Begin
ClrScr;
root:=Nil;
Writeln(‘Введіть текст’);
Read(k);
While k<>0 Do
Begin
Search(k, root);
Read(k)
End;
Printlist(root);
Repeat Until KeyPressed
End.
Процедура Printlist – приклад проходу по списку.
Процедура пошуку нагадує пошук у масивах та файлах. При пошуку у списку можна також використовувати фіктивний елемент чи бар’єр.
Більш ефективно робити пошук в упорядкованому списку, так як перегляд завершується одразу ж після знаходження елементу. Упорядкованості списку можна досягти завдяки включенню нових елементів при створенні списку не у початок, а у відповідні в залежності від значення ключа місця.
Завдяки легкості включення в зв’язаний список упорядкованість забезпечується майже без додаткових витрат. Масиви та файли такої можливості не надають.
Наведена нижче рекурсивна процедура створює список, що складається з цiлочислових значень, упорядкований за зростанням значень елементів.
{Включення елемента d у вiдповiдне мiсце списка p, таким чином, щоб список був упорядкованим за зростанням значень}
Procedure Add_Element (Var p:Point_typ; d:Data_typ);
Var
q : Point_typ;
Begin
If p=Nil Then
Begin
New (q);
q^.next:=Nil;
q^.data:=d;
p:=q;
End
Else
If p^.data>d Then
Begin
New (q);
q^.next:=p;
q^.data:=d;
p:=q;
End
Else
Add_Element (p^.next,d)
End;