Конспекти лекцій та Лабораторні роботи з дисципліни "Алгоритми та структури даних" для III курсу спеціальності 121 "Інженерія програмного забезпечення" ОКР "Фаховий молодший бакалавр" Херсонського політехнічного фахового коледжу Державного університету "Одеська політехніка"
Динамічні структури за визначенням характеризуються відсутністю фізичної суміжності елементів структури в пам’яті, непостійністю і непередбачуваністю розміру (кількість елементів) структури в процесі її обробки.
Оскільки елементи динамічної структури розташовуються за не передбачуваними адресами пам’яті, адресу елемента такої структури не можна обчислити за адресою початкового або попереднього елемента. Для встановлення зв’язку між елементами динамічної структури використовуються покажчики, через які встановлюються явні зв’язки між елементами. Таке представлення даних в пам’яті називається зв’язним. Елемент динамічної структури складається з двох полів:
Коли зв’язне представлення даних використовується для вирішення прикладної задачі,для кінцевого користувача „видимим” робиться тільки вміст інформаційного поля,а поле зв’язку використовується тільки програмістом- розробником.
Переваги зв’язного представлення даних:
Разом з тим зв’язне представлення не позбавлене й недоліків, основні з яких:
Останній недолік є найбільш серйозним і саме ним обмежується застосування зв’язного представлення даних. Якщо в суміжному представленні даних для обчислення адреси будь-якого елемента у всіх випадках достатньо номера елемента і інформації, яка міститься в описі структури, то для зв’язного представлення адреса елемента не може бути обчислена з початкових даних. Опис зв’язної структури містить один або декілька покажчиків, які дозволяють увійти до структури, далі пошук необхідного елемента виконується проходженням ланцюжком покажчиків від елемента до елемента. Тому зв’язне представлення практично ніколи не застосовується в задачах, де логічна структура даних має вигляд вектора або масиву - з доступом за номером елемента, але часто застосовується в задачах, де логічна структура вимагає іншої початкової інформації доступу (таблиці, списки, дерева і т.д.).
Дерево- це граф, який характеризується наступними властивостями:
Назва„дерево” виникла з логічної еквівалентності дерево-видної структури абстрактному дереву з теорії графів. Лінія зв’язку між парою вузлів дерева називається гілкою. Ті вузли, які не посилаються ні на які інші вузли дерева,називаються листям. Вузол, що не є листком або коренем, вважається проміжним або вузлом галуження.
В багатьох застосування відносний порядок проходження вершин на кожному окремому ярусі має певне значення. При представленні дерева в пам’яті комп’ютера такий порядок вводиться автоматично, навіть якщо він сам по собі довільний. Порядок проходження вершин на деякому ярусі можна легко ввести,позначаючи одну вершину як першу, іншу - як другу і т.д. Замість впорядковування вершин можна задавати порядок на ребрах. Якщо в орієнтованому дереві на кожному ярусі заданий порядок проходження вершин, то таке дерево називається впорядкованим деревом.
Введемо ще деякі поняття, пов’язані з деревами. Вузол X називається предком, або батьком, а вузли Y і Z називаються нащадками, або синами, їх,відповідно, між собою називають братами. Причому, лівий син є старшим сином, а правий - молодшим. Кількість піддерев даної вершини називається мірою цієї вершини.
Якщо з дерева прибрати коріння і ребра, що сполучають коріння з вершинами першого ярусу, то вийде деяка множина незв’язаних дерев. Множина незв’язаних дерев називається лісом.
Є ряд способів графічного зображення дерев. Перший спосіб полягає у використовуванні для зображення піддерев відомого методу діаграм Венна, другий - методу дужок,що вкладаються одна в одну, третій спосіб - це спосіб, який використовується при складанні змісту книг. Останній спосіб, що базується на форматі з нумерацією рівнів, схожий з методами, які використовуються в мовах програмування. При застосуванні цього формату кожній вершині приписується числовий номер, який повинен бути менший номерів, приписаних кореневим вершинам приєднаних до неї піддерев.
Повне дерево містить максимально можливу кількість вузлів на кожному рівні, крім нижнього. Повні дерева мають ряд важливих властивостей.
По-перше,це найкоротші дерева, які можуть містити задану кількість вузлів. Досить корисна властивість повних дерев полягає в тому, що вони можуть бути дуже компактно записані в масивах. Якщо пронумерувати вузли в „природному” порядку,зверху вниз і зліва направо, то можна помістити елементи дерева в масив у цьому ж порядку.
Існують m-арні дерева, тобто такі дерева, в яких півміра виходу кожної вершини менша або рівна т. Якщо півміра виходу кожної вершини в точності рівна або т, або нулю, то таке дерево називається повним m-арним деревом. При т=2 такі дерева називаються відповідно бінарними, або повними бінарними.
Представити m-арне дерево в пам’яті комп’ютера складно, так як кожен елемент дерева повинен містити стільки покажчиків, скільки ребер, виходить з вузла. Це приведе до підвищеної витрати пам’яті, різноманітності початкових елементів і ускладнить алгоритми обробки дерева. Тому m-арні дерева, ліс необхідно привести до бінарних для економії пам’яті і спрощенню алгоритмів. Усі вузли бінарного дерева представляються в пам’яті однотипними елементами з двома покажчиками, крім того, операції над бінарними деревами виконуються просто і ефективно.
Правило побудови бінарного дерева з будь-якого дерева:
Таким чином перебудувати дерево заправилом:
У результаті перетворення будь-якого дерева, в бінарне, виходить дерево у вигляді лівого піддерева, підвішеного до кореня.
У процесі перетворення правий покажчик кожного вузла бінарного дерева буде вказувати на сусіда по рівню. Якщо такого немає, то правий покажчик - NULL.Лівий покажчик буде вказувати на вершину наступного рівня. Якщо такої немає, то покажчик встановлюється на NULL.
Описаний вище метод представлення довільних впорядкованих дерев за допомогою бінарних дерев можна узагальнити на представлення довільного впорядкованого лісу.
Правило побудови бінарного дерева з лісу: корені всіх піддерев лісу з’єднати горизонтальними зв’язками. В отриманому дереві вузли в даному прикладі будуть розташовуватися на трьох рівнях. Далі перебудовувати по раніше розглянутому плану.В результаті перетворення впорядкованого лісу в бінарне дерево виходить повне бінарне дерево з лівим і правим піддеревом.
Дерева можна представляти за допомогою зв’язних списків і масивів (або послідовнихсписків).
Частіше всього використовується зв’язне представлення дерев, так як воно дуже сильно нагадує логічне. Зв’язне зберігання полягає в тому, що задається зв’язок від батька до синів. В бінарному дереві є два покажчики, тому зручно вузол представити у вигляді структури в якій left - покажчик на ліве піддерево, right - покажчик на праве піддерево, inf - містить інформацію, яка зв’язана з вершиною і має наперед визначений тип - data.
Над деревами визначені наступні основні операції:
Потрібна вершина в дереві шукається за ключем. Пошук в бінарному дереві здійснюється таким чином.
Нехай побудовано деяке дерево і вимагається знайти вузол з ключем X. Спочатку порівнюємо з X ключ, що знаходиться в корені дерева. У разі рівності пошук закінчений і потрібно повернути покажчик на корінь в якості результату пошуку. Інакше переходимо до розгляду вершини, яка знаходиться зліва внизу, якщо ключ X менший тільки що розглянутого, або справа внизу, якщо ключ X більший щойно розглянутого. Порівнюємо ключ X з ключем, що міститься в цій вершині, і т.д.Процес завершується в одному з двох випадків:
В першому випадку повертається покажчик на знайдену вершину. В другому - покажчик на вузол, де зупинився пошук (що зручне для побудови дерева ).
Для включення запису в дерево перш за все потрібно знайти в дереві ту вершину, до якої можна приєднати нову вершину, відповідну запису, що включається. При цьому впорядкованість ключів повинна зберігатися.
Алгоритм пошуку потрібної вершини, взагалі кажучи, той же самий, що і при пошуку вершиниіз заданим ключем. Ця вершина буде знайдена в той момент, коли в якості чергового покажчика, який визначає гілку дерева, в якій треба продовжити пошук,виявиться покажчик NULL.
В багатьох задачах, пов’язаних з деревами, вимагається здійснити систематичний перегляд всіх його вузлів в певному порядку. Такий перегляд називається проходженням або обходом дерева.
Бінарне дерево можна обходити трьома основними способами: низхідним, змішаним і висхідним (можливі також зворотний низхідний, зворотний змішаний і зворотний висхідний обходи). Прийняті назви методів обходу зв’язані з часом обробки кореневої вершини: До того як оброблено обидва його піддерева, після того, як оброблено ліве піддерево, але до того як оброблено праве, після того, як оброблено обидва піддерева. Використовувані назви методів відображають напрям обходу в дереві: від кореневої вершини вниз до листя - низхідний обхід; від листя вгору до кореня - висхідний обхід, і змішаний обхід - від найлівішого листка дерева через корінь до найправішого листка.
Схемно алгоритм обходу бінарного дерева відповідно до низхідного способу може виглядати таким чином:
Алгоритм істотно спрощується при використовуванні рекурсії. Так, низхідний обхід можнаописати таким чином:
Змішаний обхід можна описати таким чином:
Рекурсивний змішаний обхід описується таким чином:
Складність реалізації висхідного обходу полягає в тому, що на відміну від попереднього методу в цьому алгоритмі кожна вершина запам’ятовується в стеку двічі: вперше - коли обходиться ліве піддерево, і другий раз - коли обходиться праве піддерево.Таким чином, в алгоритмі необхідно розрізняти два види стекових записів: 1-йозначає, що в даний момент обходиться ліве піддерево; 2-й - що обходиться праве, тому в стеку запам’ятовується покажчик на вузол і ознаку (код-1 і код-2 відповідно).
Алгоритм висхідного обходу можна представити таким чином:
Рекурсивний змішаний обхід описується таким чином:
Якщо в розглянутих вище алгоритмах поміняти місцями поля покажчики на лівого і правого сина, то отримують процедури зворотного низхідного, зворотного змішаного і зворотного висхідного обходів.