
Лабораторна робота №2 односпрямовані і дВохСпрямовані списки
Мета роботи:
- оволодіти навичками доступу до даних і роботу з пам'яттю при використанні при реалізації списку на основі масиву;
- навчитися вирішувати завдання з використанням списків.
Теоретичні відомості
Ключові терміни:
Списком називається впорядкована множина, що складається зі змінного числа елементів, до яких застосовуються операції включення, виключення. Список, що відбиває відносини сусідства між елементами, називається лінійним. Довжина списку дорівнює числу елементів, що містяться у списку. Список нульової довжини називається порожнім списком.
2. Основні операції над списками
1. Створити порожній список createList ().
2. Знищити список destroyList ().
3. Визначити, чи порожній список isEmpty():boolean.
4. Визначити кількість елементів у списку getLength ().
5. Вставити елемент у зазначену позицію списку insert.
6. Вилучити елемент, що перебуває в зазначеній позиції списку remove.
7. Переглянути (витягти) елемент, що перебуває в зазначеній позиції списку retrieve.
3. Реализация абстрактного списка на основі масиву
Перейдемо до реалізації списку у вигляді класу.
Нам потрібний спосіб для подання елементів списку і його довжини. На перший погляд елементи списку зручно зберігати в масиві іtems. Реалізація списку у вигляді масиву - цілком природний вибір, оскільки і масив, і список зберігають пронумеровані елементи. Однак список передбачає такі операції, яких немає у масива, наприклад операцію getLength.
Скільки комірок масиву займе список? Може бути, весь масив, а може і ні. Максимальна довжина масиву, тобто його фізичний розмір (physіcal sіze), буде відомий і задаватиметься константою MAX_LІST, Для відстеження поточної кількості елементів списку, записаних у масиві, тобто його логічного розміру (logіcal sіze) , будемо використати змінну sіze.
Переваги цього підходу очевидна - реалізація функції getLength буде дуже простій. Отже, для реалізації можна застосувати наступний код.
const int MAX__LIST = 100; // Максимальная довжина списку
typedef int ListItemType; // Тип элементов списка
ListItemType items[MAX_LIST]; // Масив елементів списку
int size; // Довжина списку
На рис. 1 показані дані-члени реалізації абстрактного списку цілих чисел у вигляді масиву.
Рис. 1. Реалізація списку у вигляді масиву
Для того щоб вставити новий елемент у задану позицію масиву, потрібно зсунути всі елементи, що перебувають праворуч, на одну позицію і вставити новий елемент на місце, що звільнилося. Ця операція показана на рис. 2.
Рис. 2. Зсув елементів масиву для вставки нового елемента списку в третю комірку
Розглянемо тепер процедуру вилучення елемента зі списку. Його можна просто стерти, однак це приведе до утворення порожнеч у масиві, як показано на рис. 3, а. Масив, що містить порожнечі, породжує наступні проблеми:
- значення sіze - 1 більше не дорівнює останньому індексу масиву. Для його відстеження потрібна нова змінна, lastPosіtіon;
- оскільки елементи розкидані, функція retreіve повинна перевіряти кожну комірку масиву, навіть якщо він містить усього кілька елементів.
- Якщо комірка іtems [MAX_LІST - 1] зайнята, список може здатися повним, навіть якщо кількість його елементів набагато менше константи МАХ_LІST.
Рис. 3. Вилучення елемента зі списку: а) вилучення, що породжує порожнечі; б) заповнення порожнеч шляхом зсуву елементів
Отже, зсунуті елементи, заповнюючи порожнечі, що утворилися, як показано на рис. 3, б, дійсно необхідні.
Отже, для реалізації абстрактного списку на основі зазначених операцій необхідно
- вибрати відповідну структуру даних;
- визначити і реалізувати клас у заголовному файлі. Операції над абстрактним типом визначаються як відкриті функції-члени класу, а дані АТД звичайно оголошуються в закритому розділі класу.
- у файлі реалізації необхідно реалізувати всі функції-члени класу.
Програма, що використає такий клас, зможе одержати доступ до даних тільки за допомогою операцій над абстрактним типом.
Детальне вивчення реалізації абстрактного списку у вигляді масиву показує, що масив не завжди підходить для зберігання набору даних. Масив має фіксований розмір (принаймні в більшості мов програмування), у той час як довжина абстрактного списку не обмежена. Таким чином, строго говорячи, масив краще не використовувати для реалізації списку, оскільки потенційна кількість елементів списку може перевершити фіксований розмір масиву. Ця проблема часто виникає при реалізації абстрактних типів даних. У багатьох випадках слід віддати перевагу реалізації зі змінним розміром.
Розробники інтуїтивно прагнуть зберігати дані в послідовно розташованих комірках, хоча такий підхід має ряд недоліків. У цьому випадку елементи розташовуються в суміжних комірках. Як ми вже бачили, це приводить до того, що при виконанні операцій вставки і вилучення доводиться зсувати елементи масиву, витрачаючи додатковий час. Подивимося, які альтернативні рішення цієї проблеми.