Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
О.О.П / ооп / 4_кол / К курсовой / Методи побудови алгоритмів та їх аналіз / Інформатика_1 (методи побудови алгоритмівта та їх аналіз).doc
Скачиваний:
0
Добавлен:
30.05.2020
Размер:
2.5 Mб
Скачать

Дійсні числа

Найчастіше для опису дійсних чисел використовуються такі типи: real (6 байт), single (4 байт), double (8 байт), extended (10 байт).

Як приклад розглянемо розташування в пам'яті значень змінних типу single. Нагадаємо, що дійсні числа в комп'ютері представляються в показниковій формі. Наприклад, число -1.0123 • 10~15 виглядатиме так (мал. 4):

55

-10123 Е-16

t t ft

знак знак числа порядку

мавгиса порядок Мал. 4

Але із зазначеної на схемі інформації в пам'ять комп'ютера заноситься не все, а лише знак числа, мантиса числа (послі-довність ЇЇ цифр) та порядок. Причому все в двійковому представленні. Як бачимо, в пам'ять комп'ютера не заноситься знак порядку дійсного числа. Для уникнення проблеми запа-м'ятовування знака порядку до їх значень додаеться констант-не зміщення, яке робить їх завжди додатними. Ми не будемо детально розглядати щ" питания, оскільки вони є предметом іншого, більш глибокого вивчення. Схематично побітне пред­ставления короткого дійсного числа в пам'яті комп'ютера мож-на зобразити так (табл. 8):

Таблиця8 Біти

31

24

23 ... 0

Знак

Порядок

Мантиса

Текстова інформація Символи

Значениями змінних типу char є довільні символи (літери, цифри, знаки операцій, коми, дужки тощо), які використо-вуються в даній мові програмування. Значения цього типу вважаються впорядкованими. Спосіб упорядкування залежить від машинної реалізації мови, однак завжди вважається, що цифри упорядковані за зростанням, а літери - за абеткою. Ко-дування упорядкованих символів починається від 0 до 255. Це пояснюється тим, що значения зміиних цього типу займають у пам'яті комп'ютера 1 байт (111111112 = 25510). Таким сим­волом може бути будь-який символ із таблиці ASCU-кодів.

Наприклад, символ 'А' латинської абетки у таблиці ASCII-кодів позначений кодом 65 (65J0 = 10000012). Тому при введенні цього символу в пам'ять комп'ютера запипіеться така послі-довність двійкових цифр (табл. 9):

56

Таблиця9

Байт

7

6

5

4

3

2

1

0

0

1

0

0

0

0

0

1

Рядки

Рядкова інформація або текстові дані представляються у вигляді послідовності двійково-кодованих символів і можуть займати декілька байтів, тобто стільки байтів, скільки сим-волів входить до цього рядка. У Pascal на довжину рядкових ве­личин накладаеться обмеження у 255 символів.

Наприклад, символи абревіатури 'ПК' будуть кодуватися так: 'П' - 14310 = 100011112; 'К' - 13810 = 100010102. У пам'яті комп'ютера вони розташуються так, як показано в таблиці 10.

Таблиця 10

Байт h + 1

Байт fe

15

14

13

12

11

10

9

8

7

6

5

4

3

2

/

0

1

0

0

0

1

0

1

0

1

0

0

0

1

1

1

1

К

п

3 таблиці 10 можна зробити висновок, одо оскільки байти нумеруються справа наліво, то таким же чином розташовують-ся і коди відповідних символів.

/ Запитання для самоконтролю

  1. Як порозрядно в пам'яті комп'ютера представляються значения цілих чисел: однобайтові, двобайтові, чотирибайтові?

  2. Як визначається діапазон зміни значены цілих чисел?

  3. Як порозрядно в пам'яті комп'ютера представляються значения дійсних чисел?

  4. Як визначається діапазон зміни значень дійсних чисел?

  5. Як представляється в пам'яті комп'ютера символьна інфор-мація?

  6. Як представляється в пам'яті комп'ютера рядкова інформація?

57

Розділ III

СТРУКТУРИ ДАНИХ

Основним завданням будь-якого алгоритму є обробка інфор-мації. Яким чином представити інформацію? Адже від зручно-го способу її представления залежить і зручність обробки.

Наприклад, структура телефонного довідника або шкільно-го журналу нам знайома і уявити її іншою складно. Зрозуміло, що, переписавши телефонний довідник у вигляді звичайного тексту, ми унеможливимо пошук у ньому інформації.

Записуючи перелік деяких завдань, ми, не замислюючись, використовуємо таку структуру, як список, нумеруючи його пункти або позначаючи їх якимись символами. А от листа ми пишемо у вигляді звичайного тексту. Але і тут виділяємо ок-ремі смислові абзаци для зручності його читання.

Отже, перш ніж починати обмірковувати питания опрацювання інформації, треба визначитися, як ЇЇ представити. Найчастіше на практиці обидва ці продеси взаємопов'язані. Тобто інколи під час роботи з інформацією нам доводиться змі-нювати, вдосконалювати те представления, що було обране спочатку.

Структура даних - це спосіб представления інформації.

В алгоритмізації знання різноманітних структур даних, ефективне їх використання мають неабияке значения. Правильне, коректне визначення структур даних, що використовуються в алгоритмі, - це значна частина успіху.

Деякі структури даних визначаються безпосередньо в мовах програмування, які ми використовуємо для реалізації алгорит-мів. Наприклад, ви вже знайомі з простими змінними, масивами. А деякі організовуються самими розробниками алгорит-мів. Саме з ними ми маемо ознайомитися в цьому розділі.

Можна сказати, що структури даних с одним з інструментів розробки алгоритмів. Багато відомих методів алгоритмів ба­зу ються на тих чи інших структурах.

Ознайомлюючись із структурами данях, ми, в першу чергу, будемо виділяти два такі важливі моменти:

  • відображення структури даних на пам'ять комп'ютера;

  • обробка інформації в даній структурі.

58

Перше питания пов'язане з визначенням принципу збереження даних у пам'яті комп'ютера, а друге - з організацією читання та запису інформації. Слід звернути увагу на те, що в деякій літературі читання інформації трактується як її ви-лучення.

Також не менш важливою є інформація про доступ до еле-ментів кожної структури: прямий чи послідовний.

Якщо до будь-якого елемента заданої структури можна ді-статися, не переглядаючи всі, то такий доступ називають прямим. У протилежному випадку — послідовним.

ПРОСТА ЗМІННА

Під час виконання програми, в якій визначено просту змінну, операційною системою в пам'яті комп'ютера відводиться місце для її значения. При цьому визначається адреса, за якою вона буде розташована, та частина підряд розташованих комірок, кількість яких відповідає визначеному для цієї змінної типу.

Отже, тепер протягом виконання програми з ім'ям описаної змінної пов'язана конкретна адреса в пам'яті комп'ютера та відповідна до її типу кількість байтів, що починаються з цієї адреси.

Ми визначилися з відображенням простої змінної на пам'ять комп'ютера. Тепер розглянемо, як відбувається запис і читання значень цієї найпростішої структури даних.

При виконанні дій, робота яких пов'язана із записом значения змінної у відповідну область пам'яті, тобто стандартних процедур read, readln та оператора присвоювання змінній деякого значения, відбувається визначення адреси, що відповідає розміщенню значения даної змінної в пам'яті комп'ютера і запис указаного двійкового значения за цією адресою.

Виконання операції читання значения простої змінної відбувається при використанні стандартних процедур write, writeln та обчислення значень виразів, у яких використовуються ідентифікатори цих змінних. При цьому система «знає» адресу розміщення в пам'яті значения ціеї змінної і «читає» його за цією адресою. Далі відбувається процес декодування двійкового зображення інформації, що відповідає правилу її кодування згідно з указаним для цієї змінної типом.

Можна зробити висновок, що прості змінні є структурою прямого доступу.

Запитання для самоконтролю

  1. Що називають структурами даних?

  2. Чому виникає необхідність організації різних структур даних?

3. Які два основні моменти пов'язані з визначенням структури даних?

4. Які основні дії виконуються над елементами структури даних?

5. Який доступ до елементів заданоі структури називають прямим?

6. Який доступ до елемєнтів задано! структури називають послі-довним?

7. Яким чином проста змінна відображається на пам'ять комп'ютера?

8. Як відбувається читання значения змінної простого типу?

9. Як відбувається запис значения змінної простого типу?

10. Який принцип доступу здійснюється до значень простих змінних при їх обробці? Обґрунтуйте свою відповідь.

МАСИВ

3 масивами ми вже ознайомилися раніше і набули певного досвіду роботи з ними. Тому нашим завданням є перевалено уточнения питания розташування масивів у пам'яті комп'ютера та алгоритму доступу до їх елементів при виконанні опера-цій читання і запису.

Розглянемо спочатку одновимірні масиви.

Спершу нагадаемо, що пам'ять комп'ютера є дискретною і мае лінійну структуру. Тобто, всі комірки пам'яті мають абсолютні адреси, що починаються з 0. Для зручності адреси комірок пам'яті комп'ютера представляють шістнадцятковими числами: 0, 1, 2…9, А, В, С, D, Е, F, 10, .... FF, ... .

Саме тому відображення елементів одновимірного масиву на пам'ять комп'ютера уявити собі зовсім нескладно: вони записані в пам'ять підряд, починаючи з деякого визначеного місця.

При розташуванні масиву ava2, .... ап у пам'яті комп'ютера визначається адреса його початку, тобто першого елемента масиву av і далі розміщуються всі елементи, займаючи кож-ний стільки байтів, скільки визначено вказаним типом. Тобто, система «знає» адресу першого елемента масиву, скільки байтів займає кожний елемент і загальну кількість елементів масиву.

Адреса розміщення будь-якого іншого елемента а, визначається за таким алгоритмом: до адреси першого елемента додається кількість байтів, що займає кожний елемент, помножена на порядковий номер потрібного елемента. Таким чином, існує алгоритм обчислення адреси будь-якого елемента одновимірного масиву. Перевага такого принципу безперечна: системі не треба знати адресу кожного елемента масиву, оскільки її можна обчислити за допомогою елементарних арифметичних дій.

Тепер зрозуміло, як відбувається читання і запис інформації, коли використовується структура даних «масив». Спершу визначається адреса потрібного елемента масиву, а потім дії читання і запису виконуються так само, як і для простих змінних.

60

Можемо також зробити висновок, що структура даних «масив» є, як i проста змінна, структурою прямого доступу.

Розташування елементів двовимірного масиву aij, де i = 1, 2, ..., п; j = 1, 2, ..., т у пам'яті комп'ютера відбувається так: спочатку розміщуються елементи першого рядка, потім другого і т. д.

Так само як і для одновимірного масиву, при визначенні місця розташування елементів двовимірного стає відомою адреса першого елемента. Адреса будь-якого іншого елемента аij цього масиву визначається за алгоритмом:

<адреса аij> = <адреса а11> + ((і - 1) • m + j - 1) • <кількість байтів указаного типу>.

Тобто для визначення адреси поточного елемента двови-мірного масиву потрібно до адреси першого елемента додати кількість байтів, яку займає один елемент указаного типу, по-множену на кількість елементів двовимірного масиву (і - 1) • т + j - 1, що розміщені в пам'яті комп'ютера від першого елемента до даного аij.

Елементи масивів більшої вимірності розміщуються в па-м'яті комп'ютера аналогічно.

Наприклад, тривимірний масив зручно уявити собі як па-ралелепіпед, кожен шар якого представляв собою таблицю, тобто двовимірний масив. Отже, тривимірний масив є масивом двовимірних масивів. У пам'яті комп'ютера він виглядатиме так: елементи першого «шару» тривимірного масиву будуть розташовані в пам'яті за правилом двовимірного масиву, потім так само елементи другого «шару» і т. д.

Запитання для самоконтролю

  1. Що представляє собою структура даних «масив»?

  2. Як відображається на пам'ять комп'ютера одновимірний масив?

  3. Які два основні моменти пов'язані з визначенням структури да­них?

  4. За яким алгоритмом визначається адреса будь-якого елемента одновимірного масиву?

  5. Як відбуваються в пам'яті комп'ютера операції запису і читання елементів одновимірного масиву?

  6. Яким є доступ до елементів масиву? Обгрунтуйте свою відповідь.

  7. Як розташовуються в пам'яті комп'ютера елементи двовимірно-го масиву?

  8. Як обчислюється адреса будь-якого елемента двовимірного ма­сиву?

  9. Яким чином розташовуються в пам'яті комп'ютера елементи масивів більшої розмірності?

t

61

СТЕК

Стеком називається структура даних, що організована за принципом «останнім прийшов — першим пішов».

3 поняттям стека ми вже зустрічалися при ознайомленні з алгоритмом роботи рекурсії.

Ми домовилися розглядати структури даних у відображенні на пам'ять комп'ютера. Тому в першу чергу треба визначитися, яким чином елементи даної структури зберігаються в пам'яті.

Оскільки організація одновимірного масиву аналогічна лі-нійній структурі пам'яті комп'ютера, то логічним буде пред­ставления стека саме у вигляді одновимірного масиву (мал. 5).

а1

а2

аз

...

а4

Мал. 5

А от організація обробки елементів цього масиву буде такою, що відповідає означению цієї структури даних. Для простішого розуміння поняття «стек» проведемо аналогію зі стосом книг.

Додаючи нову книжку в стос, ми кладемо її зверху, тобто операщя запису нового елемента в стек відбувається в кіпець масиву додаванням нового елемента. А для того щоб узяти книжку із середини стосу, нам треба всі верхні книжки по од-ній зняти у зворотному порядку щодо їх складання. Таким чином, операщя читання зі стека відбувається також із кінця масиву, але в зворотному порядку.

Обробляючи елементи масиву, ми вводимо поняття поточно­го порядкового номера елемента масиву L В даному випадку нас цікавитиме лише останній елемент: ми його або зчитуємо ( ), або після нього записусмо новий елемент ( ).

Індекс останнього елемента стека називається вершиною. Це означає, що для обробки елементів стека нам достатньо знати значения лише однієї величини і вершини стека. Схематично стек можна зобразити так (мал. 6):

і

а1

а2

ai

an

Мал. 6

Подвійною стрілкою ( ) ми позначили елемент вершини стека, який доступний як для читання, так і для запису, a сірим кольором - ті елементи масиву, які належать стеку. Нижня стрілка показує зміну розміру стека в межах визначеного масиву.

Алгоритм запису нового елемента х у стек виглядатиме так:

I := 1 + 1; аі:=х.

Тобто ми спочатку збільшуємо значения вершини стека на 1, а потім елементу масиву з індексом і присвоюємо значения х. Алгоритм читання чергового елемента стека буде таким:

хі := а,;

і := 1-1.

Пояснимо цей алгоритм: спочатку значения елемента стека, що знаходиться на його вершит і, читаемо в х, а потім змен-шуємо значения вершини стека на 1.

Тепер, коли ми визначили операції для роботи з елементами стека, обов'язково треба зауважити таке.

Для швидкодії роботи зі стеком не витрачаеться час на *знищення» елементів стека, які еже «вичитано». Доступ до цих елементів утрачається лише за рахунок зміни значения вершини стека (і - 1). Таким чином, у масиві лишається «сміт-тя». При наступному «попаданні» на ці елементи в масиві під час виконання операції запису туди будуть записані нові зна­чения.

Розглянемо тепер реалізацію цих алгоритмів мовою Pascal. Перш за все треба обговорити критичні ситуації, які можуть виникнути при читанні та записі інформації. А саме: при чи-танні може виникнути ситуація, коли вже читати немае чого, тобто стек порожній, а при записі - стек переповнений, тобто досягнуто останнього елемента масиву, оскільки, описуючи масив у програмі, ми повинні вказати межі зміни його індексу.

Процедура читання зі стека:

procedure read_from_stack; begin

if i = 0 then writeln('Stack is empty') else begin writeln(a[ij); dec(i) end end;

Логічним є твердження, що на початку роботи програми з обробки елементів стека вершина стека повинна мати значен-

г

63

ня 0, оскільки в початковому стані дозволена лише операція за-пису в стек.

Процедура запису в стек може виглядати так:

procedure write_to_stack; begin

if i > = n then writeln('Stack is full') else begin inc(i)

readln(a[i]); end end;

При виконанні програми, що реалізує роботу зі стеком, нам цікаво спостерігати за вмістом стека під час виконання операцій читання та запису. Для цього можна скористатися процедурою виведення поточного вмісту стека:

procedure printstack; vark: word; begin

for k := 1 to i do write(a[k],' '); writeln end;

Корисною також є інформація про вміст усього масиву, від-веденого для організації стека, під час обробки елементів стека. Саме при виконанні цієї процедури можна спостерігати за появою «сміття» в масиві та заміною його на нові значения при виконанні оперяції запису в стек.

procedure printmas; var i: word; begin for i := 1 to n do

write(a[i], ' '); writeln end;

Нам залишилося ще визначитися з принципом доступу до елементів даної структури. Робота зі стеком зводиться до об­робки вершинного елемента, який доступнии завжди, а решта елементів - ні. Щоб зробити доступним деякий елемент ак (ft < i), треба спочатку вичитати зі стека всі елементи, що зна-ходяться вище від нього, зробивши таким чином даний еле­мент вершиною стека. Тому можна зробити висновок: структу­ра даних «стек» є структурою послідовного доступу.

64

• Запитання для самоконтролю

  1. Дайте означения структури даних «стек».

  2. Як структура даних «стек» відображається на пам'ять комп'ютера?

  3. Що називається вершиною стека?

  4. Зобразіть схематично роботу з елементами стека.

  5. Як відбувається операція запису елемента в стек?

  6. Запишіть алгоритм запису елемента в стек.

  7. Як відбувається операція читання елемента зі стека?

  8. Запишіть алгоритм читання елемента зі стека.

  9. Яким чином досягається швидкодія роботи зі стеком?

  1. Запишіть процедуру запису в стек.

  2. Запишіть процедуру читання зі стека.

  3. Запишіть процедуру перегляду елементів стека.

  4. Запишіть процедуру перегляду елементів масиву, на який відоб-ражається вміст стека.

  5. Який принцип доступу здійснюється до значень елементів стека при їх обробці? Обґрунтуйте свою відповідь.

Завдання

1. Розробити діалогову меню-орієнтовану програму роботи з елементами стека за такими пунктами:

  1. записати елемент у стек;

  2. прочитати елемент зі стека;

  3. показати вміст стека;

  4. показати вміст масиву;

  5. завершити роботу зі стеком.

2. Протестувати програму завдання 1 для масиву розмірністю п - 5, спостерігаючи за вмістом стека та масиву, за такою схемою:

  • заповнити повністю стек;

  • спорожнитк стек;

  • заповнити стек до половини;

  • вичитати один елемент;

  • записати два елементи.

3. Зробити письмовий аналіз виконання завдань 1,2.

I

ЧЕРГА

Чергою називасться структура даних, организована за принципом «першим прийшов — першим ПІІІІ0В».

Черга, так само як і стек, відображається на пам'ять ком­п'ютера у вигляді одновимірного масиву (мал. 7).

а>

а2

аз

...

ап

Мал. 7

.4 Іпформятпка. в Iі > к. і

65

Принцип обробки елементів черги схожий на звичайне фор-мування черги в магазині. В кожний момент часу обслуговуеть-ся перший покупець. Шсля обслуговування він іде з черги і на його місце стае наступнии — тепер він перший. Новий покупець стае в кінець черги.

Якщо цю схему перенести на обробку елементів черги, то, на перший погляд, треба знати лише індекс останнього елемента черги, щоб після нього можна було записати новий елемент. Початок черги при цьому завжди буде збігатися з першим еле-ментом масиву (мал. 8).

п

ах

аг

...

аі

...

ап

Мал. 8

Але при такій логіці існує один суттєвий недолік: після ви-бування з черги першого елемента всі решта повинні зсуватися на одну позицію вліво. Тобто потрібно кожний раз при вико-нанні операції читання виконувати таку послідовність дій:

а, := а1 + 1 для і = 1, 2, ..., <індекс кінця черги >.

Зрозуміло, що така схема обробки операції читання забирає багато часу. Для його економії застосуємо ідею читання елемен­та стека. Тобто після читання елемента не будемо намагатися знищити його значения, а просто перемістимо індекс початку черги на наступнии елемент. Таким чином ми приходимо до ідеї існування двох індексів: і - початок черги, який ще носить назву «голова» черги, та у - кінець черги, що називаеться «хвостом» черги. Тепер схематично чергу можна зобразити так (мал. 9):

-

...

а,

aj

...

ап

Мал. 9

На схемі сірим кольором позначена та частина масиву, в якій розміщені елементи черги.

Подамо алгоритм запису в чергу елемента х:

j:=j+1; а,:=х.

Тобто ми спочатку збільшуємо значения індексу «хвоста» черги на 1, а потім елементу масиву з індексом j присвоюємо значения х.

66

Алгоритм читання елемента черги виглядатиме так:

х := а,; i := i + 1.

Тобто значения «голови» черги читаемо вх, в потім збіль-пгуємо індекс «голови» черги на 1.

Тепер можна уявити собі, що наша черга ніби повзає по ма-сиву, переміщуючись від першого елемента до останнього. Причому елементи черги не розриваються і знаходяться в тій послідовності, в якій записувалися. Можна говорити, що запис у чергу буде неможливий, тобто черга переповнена, коли «хвіст» досягне останнього елемента масиву, а читання стане неможливим, тобто черга порожня, коли «голова» пережене «хвіст».

Запишемо в термінах такого тлумачення процедуру читання елемента черги:

procedure read_from_queue; begin

if i > j then writelnf 'Queue is empty') else begin

writeln(a[i]); inc(i) end end;

Процедура запису елемента в чергу:

procedure write_to_queue; begin

if j = n then writeln('Queue is full') else begin inc(j); readln(a[j]) end end;

Але це досить спрощений погляд на ефективне використан-ня елементів масиву для розміщення черги, адже в масиві мо-жуть залишатися елементи, які не є на даний момент елемента-ми черги. Розглянемо кілька конкретних ситуацій.

Нехай виконуємо лише операції запису в чергу. При цьому «голова» черги весь час знаходиться на першому елементі ма­сиву, а «хвіст» зміщується в сторону останнього. Коли буде до-сягнуто кінця масиву, це означатиме, що черга переповнена і наступні операції запису стануть неможливими. Це видно з на-ведених вище процедур запису та читання.

67

Однак ситуація зміниться, якщо поряд із операціями запису виконуватимемо і операції читання з черги. На перший погляд здається, що запис стане неможливим, коли «хвіст» черги до-сягне останнього елемента масиву. Але на початку масиву є вільні місця, що залишилися після виконання читання з черги (і > 1). Чому б їх не використати під елементи черги? Для цьо-го треба лише виконати операцію j := 1. Наша черга ніби завер-неться своїм хвостом на початок масиву. Тепер ознакою того, що черга переповнена, буде ситуація, коли «хвіст» дожене «го­лову». Схематично це виглядатиме так (мал. 10):

УІ И

ах

...

а1

...

аі

...

ап

Мал. 10

Отже, перед тим як розглянути удосконалені процедури за­пису в чергу та читання з неї, введемо деякі домовленості. Це пов'язане з тим, що конкретна реалізація алгоритму мовою програмування не дає можливості використовувати певні умов-ності. А саме, ми зазначали вище, що виконання умови i - j свідчить про те, що черга порожня, а у - і - про те, що черга пов-на. Але з погляду будь-якої мови програмування ці дві умови абсолютно ідентичні, тому для їх «розпізнавання» потрібно ввести додаткові ознаки. Однак нашим завданням не є завер­шена реалізація всіх алгоритмів, оскільки кожний програміст мае свій власний «почерк» реалізації алгоритмів. Тому умову і<І замінятимемо виразом <«голова» передує «хвосту»>, а j < і - виразом <«хвіст» передує «голові»>.

Тепер запишемо удосконалену процедуру запису в чергу:

procedure write_to_queue; begin

if (j = i) and (<«хвіст» передує «голові» > ) then writeln('Queue is full') else begin

readln(a[j]); if j = n then begin

| j := 1; <«хвіст» передує «голові»> end else inc(j); end end;

68

Аналогічно можна поставити запитання і щодо «голови» черги, коли вона досягне останнього елемента масиву. Якщо «хвіст» не збігається з початком масиву, тобто там ще є місце для нашої черги, то чому б не почати відлік «голови» з початку масиву. Тобто якщо і > п, то потрібно виконати операцію і:- 1.

Удосконалимо і процедуру читання з черги:

procedure read_from_queue; begin

if (i = j) and (<«голова» передує «хвосту»>) then writeln ('Queue is empty') else begin

writeln(a[i]); if i = П then begin

| i := 1; <«голова» передує «хвосту»> end else inc(i); end end;

Ми можемо сказати, що удосконалюючи алгоритм обробки елементів черги, зробили її кільцевою.

Процедуру перегляду елементів черги можна записати в та­кому вигляді:

procedure printqueue; var k: word; begin

if <«голова» передує «хвосту» > then

for k := i to j - 1 do write(a[k],'') else begin

for k := i to n do write(a[k],''); for k := 1 to j - 1 do write(a[k],''); end; writeln end;

Залишилося лише визначитися з принципом доступу до еле-ментів черги. Зрозуміло, що в кожний момент часу ми можемо здійснювати операції читання та запису над елементами черги, що знаходяться в «голові» або в «хвості» черги. Дістатися до деякого «внутрітнього» елемента черги ми можемо, лише ви-конавши відповідну кількість операцій читання з «голови» черги. Тому можна зробити висновок, що черга є структурою послідовного доступу.

69

Запитання для самоконтролю

  1. Яка структура даних називається чергою?

  2. Як структура даних «чєрга» відображається на пам'ять ком-п'ютера?

  3. Який елемент називають «головою» черги?

  4. Який елемент називають «хвостом» черги?

  5. Як відбувається читання елементів черги? Запишіть алгоритм читання елемента з черги.

  6. Як відбувається запис елементів у чергу? Запишіть алгоритм за-пису елемента в чергу.

  7. Яким чином організовано ефективну обробку елементів черги?

  8. Зобразіть схематично роботу з елементами черги.

  9. Запишіть процедуру запису елемента в чергу.

  1. Запишіть процедуру читання елемента з черги.

  2. Запишіть процедуру перегляду елементів черги.

  3. Який принцип доступу здійснюється до значень елементів черги при їх обробці? Обґрунтуйте свою відповідь.

Завдання

1. Розробити діалогову меню-орієнтовану програму роботи з елементами черги за такими пунктами:

  1. записати елемент у чергу;

  2. прочитати елемент із черги;

  3. показати вміст черги;

  4. показати вміст масиву;

  5. завершити роботу з чергою.

2. Протестувати програму завдання 1 для масиву роз- мірністю п = 6, спостерігаючи за вмістом черги і масиву, за та­ кою схемою:

  • заповнити повністю чергу;

  • спорожнити чергу;

  • заповнити чергу до половини;

  • вичитати один елемент;

  • записати два елементи;

  • вичитати два елементи;

  • записати чотири елементи;

  • спробувати записати п'ятий елемент;

  • вичитати шість елементів;

  • спробувати прочитати сьомий елемент.

3. Зробити письмовий аналіз виконання завдань 1,2.

ДЕК

I Деком називається структура даних, яка дає змогу додавати та вилучати елементи з обох кінців.

70

Англійською мовою дек - deque, що походить від double-ended queue, тобто дослівно «черга з двома кінцями».

Так само як і для стека та черги, основою організації дека вважатимемо одновимірний масив.

Знаючн організацію структури даних «черга», неважко удосконалити відповідні операції читання і запису та організу-вати їх у деку.

Коректна робота з даною структурою залежить ще від одно­го нового параметра: вказання лівого чи правого кінця дека, де відбуваються операції читання або запису інформації.

Оскільки ми фактично маемо двосторонню чергу, то кіль-цевий запис і читання буде відбуватися не лише з кінця масиву в початок, а й із початку в кінець (мал. 11).

_LL

«1

...

а,

...

а1

...

"■:

Мал. 11

Алгоритми читання інформації зліва та запису справа в де­ку збігаються з такими самими алгоритмами в черзі. Тому треба лише змінити відповідні операції по читанню справа та запису зліва.

Алгоритм читання справа буде виглядати так:

Dj:=j-1; 2) х := а,.

Тобто спочатку зменшуємо індекс «хвоста» черги на 1, а по-тім елементу х присвоюємо значения елемента масиву з індек-сом /.

Алгоритм запису зліва буде мати такий вигляд:

1)і:=і-1; 2)а,:-х.

Тобто спочатку зменшуємо індекс «голови» черги на 1, а потім записуємо значения елемента х в елемент масиву з індексом і.

Реалізація наведених вище алгоритмів мовою програмуван-ня Pascal може бути такою:

procedure readfrom dequejitgh; begin

if (j = i) and (<«голова» передує «хвосту»>) then writeln ('Deque is empty') else '

begin I dec(j);

71

end;

ifj = 0 then begin

| j := n; <«голова» передує «хвосту»> end; writeln (a[j]); end

procedure write_to_deque_left; begin

if (i = j) and (<«хвіст» передує «голові»>) then writeln('Deque is full') else begin dec(i); ifi = 0 then begin

I i := n; охвіст» передує «голові»> end; readln(a[i]); end end;

Так як i дві попередні структури - стек та черга, дек є струк­турою послідовного доступу, оскільки за означениям ми оброб-ляємо інформацію лише на кінцях, а решта елементів дека ста-нуть доступні лише при виконанні певної кількості операцій читання.

Ми завершили розгляд досить специфічних лінійних струк­тур даних: «стек», «черга», «дек». Ці структури називають ди-намічними, оскільки, на відміну від статичної структури даних, яким є масив, кількість елементів даних структур, тобто їх роз-мірність, протягом роботи з ними весь час змінюється. Однак слід звернути увагу на те, що при роботі з цими структурами принцип доступу до їх елементів визначений самими структура­ми, тобто існуе доступ лише до їх кінцевих елементів, що дещо обмежуе клас задач, у яких можна ці структури застосовувати.

/-

Запитання для самоконтролю

  1. Яка структура даних називається деком?

  2. Зобразіть схематично роботу з елементами дека.

  3. За яким алгоритмом відбувається читання елемента дека зліва?

  4. За яким алгоритмом відбувається читання елемента дека справа?

  5. За яким алгоритмом відбувається запис елемента в дек зліва?

  6. За яким алгоритмом відбувається запис елемента в дек справа?

  7. Запишіть процедуру читання елемента дека зліва.

72

18. Запишіть процедуру читання елемента дека справа. 9. Запишіть процедуру запису елемента в дек зліва. 10. Запишіть процедуру запису елемента в дек справа. И.Який принцип доступу здійснюється до значень елементів структури даних «дек»? Обґрунтуйте свою відповідь. 12. Якими вважаються структури даних «стек», «черга» та «дек»? Обґрунтуйте свою відповідь.

Завдання

1. Розробити діалогову меню-орієнтовану програму роботи з елементами дека за такими пунктами:

1) записати зліва елемент у дек;

2) записати справа елемент у дек;

  1. прочитати зліва елемент із дека;

  2. прочитати справа елемент із дека;

5) показати вміст дека;

6) показати вміст масиву;

7) завершити роботу з деком.

2. Протестувати програму завдання 1 для масиву розмір- ністю п - 6, спостерігаючи за вмістом дека і масиву, за такою схемою:

  • заповнити повністю дек, записуючи елементи справа;

  • спорожнити дек, читаючи елементи зліва;

  • заповнити повністю дек, записуючи елементи зліва;

  • спорожнити дек, читаючи елементи справа;

  • заповнити дек частково, чередуючи запис елементів по два то зліва, то справа;

  • вичитати один елемент зліва;

  • вичитати один елемент справа;

  • записати два елементи зліва;

  • записати два елементи справа;

- заповнити дек і спробувати записати ще один, сьомий, еле­ мент;

  • вичитати шість елементів;

  • спробувати прочитати сьомий елемент.

3. Зробити письмовий аналіз виконання завдань 1, 2.

ЗВ'ЯЗНИЙ СПИСОК

Інколи виникає потреба у доступі до елементів структури, які не є останніми. Така структура існує, і називається вона зв'язним списком.

Перш ніж дати означения структури даних «список», розгля-немо принцип виконання основних операцій над ЇЇ елементами.

Зв'язний список є динамічною структурою, що дає змогу за-писувати нові елементи в будь-яке місце вже існуючого списку

7 3

i так само читати елементи в будь-якому місці. Реально в дано-му випадку відбуватиметься вилучення цих елементів зі спис­ку, але за традицією будемо надалі продовжувати називати цю операцію читанням.

Читання елемента списку можна організувати шляхом зсу-ву вліво решти елементів, які розташовані за тим, що читаєть-ся. Однак ці дії характеризуються певним часом виконання. Ефективнішим буде шлях «небачення» цього елемента в спис­ку. Тобто якщо після (ft - 1)-го елемента в списку йшов ft-й, а після нього - (ft + 1)-й, то після вилучення ft-ro елемента за (ft - 1)-им буде слідувати (ft + 1)-й. Схематично це можна зобра-зити так (мал. 12):

(ft - 1) елемент —> ft елемент —► (ft + 1) елемент|

Мал. 12

Запис нового елемента списку в указане місце (наприклад, після ft-ro елемента) викликатиме зсув усіх елементів, що зна-ходяться після ft-ro, на одну позицію вправо. Зрозуміло, що і такий підхід не є оптимальним. Простіше було б записати його в кінець списку, але змінити послідовність перегляду елемен-тів списку: після ft-ro елемента переходити до нового елемента, записаного в кінець, а потім до (ft + 1)-го. Зобразимо схематич­но цю операцію (мал. 13):

ft елемент ■ > (ft 4-1) елемент

Мал. 13

Структуру «список» можна реалізувати на одновимірному масиві. Кожний елемент списку буде займати два сусідні еле­менти масиву, тобто таку ланку: перший - сам елемент списку, другий - індекс масиву, що вказує на місце знаходження на-ступного елемента списку (мал. 14):

ft-й елемент списку

індекс елемента масиву,

який містить наступний

елемент списку

»,

а,+ 1

74

Мал. 14

Для запису нового елемента в список треба знати індекс пер-шого вільного елемента в кінці масиву. Можлива також си­ту ація, що після певного коригування елементів списку було вилучено його перший елемент і список починається не з пер-шого елемента масиву. Тому необхідна інформаційна ланка, першим елементом якої буде індекс масиву, з якого починаеть-ся список, та індекс масиву, що визначає місце запису нового елемента списку. Домовимося також, що останній елемент списку буде вказувати на індекс масиву зі значениям 0.

Наведемо приклад заміни слова «школа» на слово «кома». Спочатку занесено у список слово «школа» (мал. 15).

3

13

ш

5

к

7

о

9

л

11

а

0

а, аг а3 а4 а5 ав а7 а8 ад а10 а„ а12 а13

Мал. 15

Тепер вилучимо, тобто прочитаємо, зі списку перший (ш) і четвертий (л) елементи (мал. 16).

5

13

ш

5

к

7

о

11

л

11

а

0

а, а2 а3 а4 а5 а6 а7 а8 а9 а10 а,, а12 а13

Мал. 16

Згідно з отриманою послідовністю читання списку ми маемо слово «коа». Можна зробити висновок, що вилучені елементи залишися в масиві, але до них немає звернення, тобто після чи­тання елементів списку в ньому залишається «сміття».

Тепер вставимо, тобто запишемо, в список після елемента «о» новий елемент «м» і отримаємо слово «кома» (мал. 17).

5

15

ш

5

к

7 1 о

13

л

11

а

0

м

11

Q! а2 «3 °4 а5 °6 а7 °8 S «10 °П °12 °13 «14 °15

Мал. 17

Неважно тепер записати алгоритми читання і запису еле-ментів у структурі даних «список» при відображенні и на одно-вимірний масив.

Операція читання виглядатиме так:

&&*г -~ а2к*4-

75

Запис нового елемента у список після ft-ro елемента можна подати такою послідовністю дій:

Da :=х;

3)а21е2:_а2-

4)a2:=a2 + 2.

Однак ефективнішим для реалізації списку є використання покажчиків (мал. 18), а саме: розташування списку в дина-мічній пам'яті, а не в статичному масиві.

Отже, тепер можемо дати означения структури даних «спи­сок».

Зв'язним списком називають структуру даних, кожний елемент якої зв'язаний з наступним за ним елементом за допомогою покажчиків.

Опишемо структуру даних «список». Про список можна сказати так: кожен його елемент представлений значениям і покажчиком на наступний елемент, який представлений зна­чениям та покажчиком на наступний для нього елемент і т. д. Тобто явним чином проглядається рекурсивний опис цієї структури: будь-який наступний елемент є таким, як і той, що на нього посилається. Специфічними є лише початок і кінець списку. Початком списку є покажчик на його перший елемент, а кінцем — покажчик у «нікуди», тобто значения покажчика повинно бути nil.

Введемо власний тип «список»:

type list = "tlist; tlist = record

data: <тип>; next: list end;

Перш ніж організовувати роботу з елементами списку, його треба створити. Створення списку - це запис початкової послі-довності елементів. Може існувати декілька способів створення списку. Основна ідея полягає в тому, щоб кожного разу допису-вати в кінець поточного списку новий елемент.

Розглянемо рекурсивну функцію, яка за відомим значениям п заповнює список, таким чином створюючи його:

76

function create_list(n: integer): var q: list;

x: integer; begin

if n = 0 then q := nil else begin writelnCInput data:'); read (x): new (q); with q" do begin

I data := x; next := create list (n - 1); end; end; createlist := q; end;

Звернення до функції створення списку в основній програмі може бути таким:

а := createjist (n).

Ще одним варіантом створення списку може бути викорис-тання такої процедури:

procedure createjist (var х: list); begin

new (x\next);

x := x'.next;

write('lnput data:');

readln (x\data); end;

Наведемо деякі пояснения щодо роботи даної процедури. Послідовність дій виглядає так.

1) Процедура new(x'.next) визначає адресу в динамічній пам'яті, за якою далі записуватиметься значения нового еле- мента.

2) Далі здійснюється перехід до цього наступного елемента, що розташований на новою адресою:

х := x'.next;.

3) Новий елемент, що знаходитиметься за адресою x'.next, стає останнім, тобто в нього записуеться значения: read(x'.data);.

Використати процедуру createlist для створення списку можна, організувавши цикл:

new (а); {Визначення адреси «голови» списку.)

{Коліювання адреси «голови» списку} х := а; {в змінну х для подальше? роботи з нею.}

77

writef Input data: '); (Введения значения першого елемента списку.}

readln (x\data);

{Введения решти елементів списку за допомогою}

for і := 1 to n - 1 do {передавання у процедуру створення поточно!}

createjist (х); {адреси останнього елемента списку.}

x'.next := nil; {Визначення для останнього елемента ознаки кінця списку.}

Порівняємо два наведет способи створення списку. Пер­ший - рекурсивна функція - створює список, використовуючи стекову пам'ять. Другий - процедура - дописує в кінець спис­ку новий елемент, що задається користувачем. Переваги і не-доліки кожного з цих підходів такі:

- функція вимагає використання додаткової стекової пам'я- ті, але може створювати порожній список, якщо п = 0;

- процедура не вимагає додаткової стекової пам'яті, але передбачає на початку введения хоча б одного елемента списку.

Можна сказати, що кожний із цих варіантів мае право на існування.

Тепер варто розглянути процедуру перегляду елементів створеного списку. Для цього можна запропонувати таку про­цедуру:

procedure show (х: list); begin

while х О nil do begin

write(x".data,''); x := x'.next end end;

Пояснимо деякі окремі фрагменти наведено! процедури.

Цикл while x О nil do дає можливість «пробігти» всі еле-менти списку, які вже в ньому є. Цей перебіг елементами спис­ку досягається виконанням у циклі оператора х := x'.next. Тобто у змінну х, що мае містити адресу розміщення в пам'яті на-ступного елемента списку, пересилається адреса, записана у полі next даного елемента списку, що якраз і вказує на нього. Завершуеться цикл тоді, коли значениям х стае nil, тобто по-точний елемент списку є кінцевим.

Зауважимо, що для захисту попередньої та всіх наступних процедур, які працюють зі списком, варто передбачити пере-вірку наявності у списку елементів. Для цього можна додати в основну програму оператор

if а = nil then writeln ('Список порожній. ДіТ з ним неможливі');

Розглянемо процедуру читання (вилучення) елемента зі списку.

78

Може бути кілька варіантів читання елемента списку: чи­таемо вказаний елемент або читаемо елемент, розміщений піс-ля вказаного. Перш за все у будь-якому з цих варіантів треба спочатку проглянути список до заданого елемента. Подібний цикл ми вже використовували раніше.


Тепер послідовність перегляду елементів списку виглядати-ме так (мал. 20):



Як вилучити елемент, що знаходиться після заданого? Для цього треба лише значения покажчика цього наступного еле­мента скопіювати на місце покажчика заданого елемента (мал. 19):

Запишемо процедуру читання елемента списку, що знахо­диться після заданого елемента зі значениям I:

procedure deleteafter (х: list); var I: integer; begin

writef Delete after what? ');

read (I);

while x".data <> I do x := x'.next;

xn.next := x".next".next end;

Для читання самого елемента зі значениям I спочатку треба переглянути список для його знаходження. В попередній на-веденій процедурі ми зупинялися саме на цьому елементі, але вилучали наступний. А як вилучити той елемент, на якому зу-пинилися? Давайте міркувати так. «Попередній елемент нам потрібний?» - «Так». - «Поточний елемент нам потрібний?» -«Hi». - «Наступний елемент нам потрібний?» - «Так». Тож да­вайте на місце поточного елемента скопіюємо всю інформацію про наступний елемент: його значения і значения покажчика на елемент, що слідує за ним. Схематично це виглядатиме так (мал. 21):

79

А тепер реалізація запропонованого алгоритму мовою Pascal:

procedure delete (х: list); var I: integer; begin

writeCDeletewhat?'); read (I);

while x'.data <> I do x := x'.next; x'.data := x".next\data; x\next := x*.next".next; end;

Мае право на існування ще одна версія алгоритму читання зі списку заданого елемента. Вона побудована на попередній про­цедур! читання наступного за задании елементом deleteafter. її смисл полягає в тому, що при перегляді списку будемо диви-тися не на поточний елемент, а на той, що слідує за ним:

while х".next".data ОI do x := x\next;

Зрозуміло, що ми зупинимося в списку на елементі, який пе­редув шуканому. Тепер процедура delete_after вилучить зі списку саме той елемент, що нам потрібний.

Розглянемо різноманітні операції запису або вставления елементів у список.

Першою розглянемо операцію запису в список елемента зі значениям I після елемента зі значениям k.

Прокоментуємо послідовність виконання операцій при за-писі нового елемента I після заданого елемента k (мал. 22). Пер­шим кроком треба визначити адресу х елемента k. Другим -

80

визначити адресу q для розміщення в пам'яті нового елемента I. Третім - скопіювати в поле next нового елемента значения по-кажчика, що записане в полі next за адресою х. Четвертям - за­писати значения I в поле data за адресою q. П'ятим - записати в поле next за адресою х значения адреси q. Запишемо текст процедури, що виконує ці операції:

procedure insert_after (x: list); var I, k: integer; begin

write('Input what:');

read (I);

write('lnput after? ');

read (k);

while x".data <> k do x := Xя.next; new (q);

q'.next := x'.next; q'.data := I;

x\next := q; end;

Запис нового елемента / перед задании елементом k вико-нується такою послідовністю дій. Спочатку знаходимо шуканий елемент k у списку. Потім визначаємо в пам'яті адресу q для но­вого елемента списку. Оскільки ми «бачимо» лише наступний, а не попередній елемент списку, то можна вдатися до таких хит-рощів. Відомо, що в результаті запису за попереднім елементом повинен іти иовий елемент, а потім елемент зі значениям k. То­му на місце нового елемента перепишемо значения елемента k i значения покажчика на наступний за ним елемент, а на звіль-нене місце - значения нового елемента I в його поле next - зна­чения адреси q. Схематично це виглядатиме так (мал. 23):

q'.data


х

Мал. 23

Наведемо текст процедури, що виконує описані операції:

procedure insert_previous (x: list); var I, k: integer; begin

| write('lnputwhat:');

81

read (I);

write('Input previous? ');

read (k);

while xA.data о к do x := xA.next;

new(q); q* := xn;

x'.data := I; x'.next := q; end;

Ми розглянули односпрямований список, тобто список, кожен елемент якого мае покажчик лише на наступний еле-мент. Існує і двоспрямований список, тобто такий, кожний елемент якого мае два покажчики: на наступний і попередній елементи списку. Список може бути і кільцевим. Для одно-спрямованого кільцевого списку останній елемент мае покаж­чик на перший або перший елемент - на останній. Для двоспря-мованого списку обидві властивості присутні одночасно.

Тепер можемо зробити логічний висновок щодо доступу до елементів структури даних «список». Це структура послідов-ного доступу, оскільки дістатися будь-якого елемента списку не можна інакше, ніж переглянувши всі елементи, що йому передують, починаючи з першого, тобто з «голови» списку. Однак ця структура значно відрізняється від попередніх — сте­ка, черги та дека, оскільки дає можливість читання та запису елементів у будь-яке місце заданої послідовності.

/ Запитання для самоконтролю

  1. Який принцип організаціі структури даних «зв'язний список»?

  2. Зобразіть схематично операцію читання елемента зв'язного списку.

  3. Зобразіть схематично операцію запису елемента зв'язного списку.

  4. Як представляеться елемент списку?

  5. Запишіть алгоритм читання елемента списку.

  6. Запишіть алгоритм запису нового елемента в список.

  7. Обґрунтуйте ефективність використання покажчиків для орга-нізації зв'язного списку.

  8. Дайте означения структури даних «зв'язний список» у термінах використання покажчиків.

  9. Наведіть два способи створення списку. Поясніть відмінність між ними та обґрунтуйте доцільність іх використання в кожній кон­кретно ситуації.

  1. Запишіть текст процедури перегляду елементів списку.

  1. Зобразіть схему читання заданого елемента списку з викорис-танням покажчиків, поясніть ідею виконання цієі операції та за-пишіть відповідну процедуру.

  2. Зобразіть схему читання елемента списку після вказаного еле­мента з використанням покажчиків, поясніть ідею виконання цієї операції та запишіть відповідну процедуру.

82

13. Зобразіть схему запису нового елемента списку після вказаного елемента з використанням покажчиків, поясніть ідею виконання

цієї операції та запишіть відповідну процедуру. 14. Зобразіть схему запису нового елемента списку перед вказаним елементом з використанням покажчиків, поясніть ідею виконан­ня цієі операції та запишіть відповідну процедуру. 15. Поясніть принцип організації односпрямованого, двоспрямова-ного та кільцевого списків. 16. Який принцип доступу здійснюється до елементів зв'язного списку при Іх обробці? Обґрунтуйте свою відповідь.

Завдання

1. Розробити діалогову меню-орієнтовану програму роботи з елементами зв'язного списку за такими пунктами:

  1. записати новий елемент у список після вказаного елемента;

  2. записати новий елемент у список перед вказаним елемен­том;

  3. прочитати заданий елемент зі списку;

  4. прочитати елемент зі списку після вказаного;

  5. прочитати елемент зі списку перед вказаним;

  6. показати вміст зв'язного списку;

  7. завершити роботу зі зв'язним списком.

2. Протестувати програму завдання 1, спостерігаючи за вміс- том зв'язного списку, за такою схемою:

  • створити список;

  • записати новий елемент після вказаного елемента в сере­дину списку;

  • записати новий елемент перед вказаним елементом у сере­дину списку;

  • записати новий елемент у початок списку;

  • записати новий елемент у кінець списку;

  • прочитати заданий елемент із середини списку;

  • прочитати елемент із середини списку після вказаного;

  • прочитати елемент із середини списку перед вказаним;

  • прочитати перший елемент зі списку;

  • прочитати останній елемент зі списку;

  • прочитати всі елементи зі списку;

  • прочитати відсутній елемент списку.

3. Зробити письмовий аналіз виконання завдань 1,2.