Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгоритмы и структуры данных / методичка структуры данных_задания.docx
Скачиваний:
54
Добавлен:
12.05.2015
Размер:
381.84 Кб
Скачать

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;