
- •1. Алгоритми та обчислювальна складність
- •1.1. Основи структури даних і алгоритми
- •1.1.2. Поняття абстрактного типу даних. Абстрактні типи даних: стеки, списки, вектори, словники, множини, мультимножини, черги, черги з пріоритетами
- •1.1.3. Кортежі, множини, словники, одно- та двобічнозв'язні списки. Реалізація абстрактних типів даних з оцінюванням складності операцій
- •1.1.4. Базові алгоритми та їх складність: пошук, сортування (прості сортування вибором, вставками, обмінами та удосконалені сортування деревом, сортування Шелла, швидке сортування)
- •1.2. Стратегії розроблення алгоритмів
- •1.2.1. Стратегія «розділяй та володарюй» та приклади застосування.
- •1.2.2. Стратегія балансування та приклади застосування.
- •1.2.3. Динамічне програмування та приклади застосування.
- •1.2.4. Оцінювання складності алгоритму під час застосування кожної стратегії
- •1.3. Моделі обчислень
- •1.3.1. Імперативний та декларативний підходи до програмування
- •1.3.2. Розв'язні, напіврозв'язні та нерозв'язні проблеми. Проблема зупинки
1.1.4. Базові алгоритми та їх складність: пошук, сортування (прості сортування вибором, вставками, обмінами та удосконалені сортування деревом, сортування Шелла, швидке сортування)
Алгоритм пошуку — алгоритм, який вирішує задачу пошуку, тобто, знаходить інформацію, яка зберігається в певній структурі даних. Структури даних можуть бути реалізовані за допомогою зв'язаних списків, масивів, дерев пошуку, хеш-таблиць чи інших методів зберігання інформації. Алгоритм пошуку на пряму залежить від структури даних, для якої він реалізований. Дуже часто алгоритм пошуку налічує особливі команди які задають структуру даних, наприклад SQL SELECT. Алгоритми пошуку класифікуються на основі їх механізму пошуку. Лінійний пошук працює достатньо повільно, відносно бінарного пошуку, але на практиці часто зустрічається. Алгоритм лінійного пошуку порівнює кожний елемент в структурі даних зі значенням котре потрібно знайти. Існує також бінарний пошук, або половинно-інтервальний пошук, однак він працює лише з відсортованими елементами, один з найшвидших алгоритмів, який повторно порівнює пошуковий елемент з центральним елементом заданої структури, якщо знайшов то кінець, якщо ні, то порівнює елементи та продовжує пошук в лівій чи правій частині структури даних поки не знайде потрібний. Реалізація алгоритму пошуку для хеш-таблиці — складне завдання, бо в такій структурі даних кожному елементу відповідає певний ключ (key), та данні відсортовані по цим ключам. Потрібно досить гарно реалізувати хеш-функцію для пошуку елементу по ключу, бо алгоритм пошуку буде шукати спочатку ключ, а потім отримуватиме значення по ключу.
Алгоритм сортування — це алгоритм, що розв'язує задачу сортування, тобто здійснює впорядкування лінійного списку (масиву) елементів.
Сортування вибором — простий алгоритм сортування лінійного масиву, на основі вставок. Має ефективність n2, що робить його неефективним при сортування великих масивів, і в цілому, менш ефективним за подібний алгоритм сортування включенням. Сортування вибором вирізняється більшою простотою, ніж сортування включенням, і в деяких випадках, вищою продуктивністю.
Алгоритм працює таким чином:
Знаходить у списку найменше значення
Міняє його місцями із першим значенням у списку
Повторює два попередніх кроки, доки список не завершиться (починаючи з наступної позиції)
Фактично, таким чином ми поділили список на дві частини: перша (ліва) — повністю відсортована, а друга (права) — ні.
Сортування включенням або сортування вставлянням — простий алгоритм сортування на основі порівнянь. На великих масивах є значно менш ефективним за такі алгоритми, як швидке сортування, пірамідальне сортування та сортування злиттям. Однак, має цілу низку переваг:
простота у реалізації
ефективний (зазвичай) на маленьких масивах
ефективний при сортуванні масивів, дані в яких вже непогано відсортовані: продуктивність рівна O(n + d), де d — кількість інверсій
на практиці ефективніший за більшість інших квадратичних алгоритмів (O(n2)), як то сортування вибором та сортування бульбашкою: його швидкодія рівна n2/4, і в найкращому випадку є лінійною
є стабільним алгоритмом
Наприклад, більшість людей при сортуванні колоди гральних карт, використовують метод, схожий на алгоритм сортування включенням. На кожному кроці алгоритму ми вибираємо один з елементів вхідних даних і вставляємо його на потрібну позицію у вже відсортованому списку доти, доки набір вхідних даних не буде вичерпано. Метод вибору чергового елементу з початкового масиву довільний; може використовуватися практично будь-який алгоритм вибору. Зазвичай (і з метою отримання стійкого алгоритму сортування), елементи вставляються за порядком їх появи у вхідному масиві.
Сортування обміном або сортування бульбашкою — це простий алгоритм сортування.
Алгоритм працює таким чином — у поданому наборі даних (списку чи масиві) порівнюються два сусідні елементи. Якщо один з елементів не відповідає критерію сортування (є більшим, або ж, навпаки, меншим за свого сусіда), то ці два елементи міняються місцями. Прохід по списку продовжується доти, доки дані не будуть відсортованими. Алгоритм отримав свою назву від того, що процес сортування за ним нагадує поведінку бульбашок повітря у резервуарі з водою. Оскільки для роботи з елементами масиву він використовує лише порівняння, це сортування на основі порівнянь.
Позиції елементів, що підлягають сортуванню відіграють велику роль у питанні продуктивності даного алгоритму. Великі елементи на початку списку не являють проблему, оскільки вони досить швидко зміщуються на свої місця. Однак, малі елементи у кінці списку переміщуються на його початок дуже повільно. Це призвело до того, що обидва типи елементів було названо кролями і черепахами, відповідно.
З метою підвищення швидкодії алгоритму, у свій час було здійснено чимало зусиль для зменшення кількості «черепах». Сортування перемішуванням є порівняно непоганим, однак, усе ще у своєму найгіршому випадку має складність O(n2). Сортування гребінцем спершу порівнює великі елементи один з одним, а вже тоді поступово переходить до все менших і менших. Його середньостатистична швидкість приблизно рівна такій в алгоритмі Швидке сортування.
Сортування двійковим (бінарним) деревом (сортування з допомогою двійкового дерева, англ. tree sort) — алгоритм сортування, що полягає в побудові двійкового дерева пошуку за ключами масиву, а далі, в створенні результуючого масиву впорядокованих елементів виконуючи обхід дерева. Алгоритм:
Отримати елементи вхідного масиву.
Побудувати двійкове дерево вставляючи елементи вхідного масиву в двійкове дерево пошуку.
Виконати обхід дерева, щоб отримати результуючий масив з впорядкованими елементами.
Сортування Шелла — це алгоритм сортування, що є узагальненням сортування включенням. Алгоритм базується на двох тезах:
Сортування включенням ефективне для майже впорядкованих масивів.
Сортування включенням неефективне, тому що переміщує елемент тільки на одну позицію за раз.
Тому сортування Шелла виконує декілька впорядкувань включенням, кожен раз порівнюючи і переставляючи елементи, що розташовані на різній відстані один від одного.
Сортування Шелла не є стабільним. Стабільним (або стійким) називається такий алгоритм сортування, що не змінює порядок елементів з однаковим ключем.
Швидке сортування (англ. Quick Sort) — алгоритм сортування, розроблений Тоні Гоаром, який не потребує додаткової пам'яті і виконує у середньому O ( n log n ) {\displaystyle \;O(n\log \;n)} операцій. Однак, у найгіршому випадку робить О(n2) порівнянь. Позаяк алгоритм використовує дуже прості цикли і операції, він працює швидше за інші алгоритми, що мають таку ж асимптотичну оцінку складності. Наприклад, зазвичай більш ніж удвічі швидший порівняно з сортуванням злиттям.
Ідея алгоритму полягає в переставлянні елементів масиву таким чином, щоб його можна було розділити на дві частини і кожний елемент з першої частини був не більший за будь-який елемент з другої. Впорядкування кожної з частин відбувається рекурсивно. Алгоритм швидкого сортування може бути реалізований як у масиві, так і в двозв'язному списку. Швидке сортування є алгоритмом на основі порівнянь і не є стабільним.
У класичному варіанті, запропонованому Гоаром,[2] з масиву обирали один елемент, а весь масив розбивали на дві частини за принципом: у першій частині — не більші за даний елемент, в другій — не менші за даний елемент. Процедура Q u i c k s o r t ( A , p , q ) {\displaystyle Quicksort(A,p,q)} здійснює часткове впорядкування масиву A з p-го по q-й індекс.
Час роботи алгоритму сортування залежить від збалансованості, що характеризує розбиття. Збалансованість у свою чергу залежить від того, який елемент обрано як опорний (відносно якого елемента виконується розбиття). Якщо розбиття збалансоване, то асимптотично алгоритм працює так само швидко як і алгоритм сортування злиттям. У найгіршому випадку асимптотична поведінка алгоритму настільки ж погана, як і в алгоритму сортування включенням.
1.1.5. Алгоритми на графах та їх складність: пошук в ширину і глибину; пошук зв'язних компонентів; побудова кістякового дерева; побудова найкоротших шляхів з виділеної вершини; побудова найкоротших шляхів між двома вершинами
Граф – це сукупність двох скінченних множин: множини точок і множини ліній, що попарно з'єднують деякі із цих точок. Множина точок називається вершинами (вузлами) графа. Множина ліній, що з'єднують вершини графа, називається ребрами (дугами) графа.
Орієнтований граф (орграф) – граф, у якого всі ребра орієнтовані, тобто ребрам якого привласнений напрямок.
Неорієнтований граф (неорграф) – граф, у якого всі ребра неорієнтовані, тобто ребрам якого не заданий напрямок.
Змішаний граф – граф, що містить як орієнтовані, так і неорієнтовані ребра.
Петлею називається ребро, що з'єднує вершину саму із собою. Дві вершини називаються суміжними, якщо існує з'єднуюче їх ребро. Ребра, що з'єднують ті самі пари вершин, називаються кратними.
Простий граф – це граф, у якому немає ні петель, ні кратних ребер.
Мультиграф – це граф, у якого будь-які дві вершини з'єднані більш ніж одним ребром.
Маршрутом у графі називається скінченна послідовність суміжних вершин і ребер, що з'єднують ці вершини.
Маршрут називається відкритим, якщо його початкова й кінцева вершини різні, у протилежному випадку він називається замкнутим.
Вибір структури даних для зберігання графа в пам'яті комп'ютера має принципове значення при розробці ефективних алгоритмів. Розглянемо кілька способів представлення графа.
1. Список ребер – це множина, утворена парами суміжних вершин (рис. 12.2). Для його зберігання звичайно використовують одномірний масив розміром m, що містить список пар вершин, суміжних з одним ребром графа. Список ребер більш зручний для реалізації різних алгоритмів на графах у порівнянні з іншими способами.
2. Матриця суміжності – це двовимірний масив розмірності n x n, значення елементів якого характеризуються суміжністю вершин графа (рис. 12.3). При цьому значенню елементу матриці привласнюється кількість ребер, які з'єднують відповідні вершини. Даний спосіб зручний, коли треба перевіряти суміжність або знаходити вагу ребра за двома заданими вершинами.
3. Матриця інцидентності – це двовимірний масив розмірності n x m, у якому вказуються зв'язки між інцидентними елементами графа (ребро й вершина). Стовпці матриці відповідають ребрам, рядки – вершинам (рис. 12.4). Ненульове значення в комірці матриці вказує зв'язок між вершиною й ребром. Даний спосіб є самим ємним для зберігання, але полегшує знаходження циклів у графі.
4. Списки суміжності. Цей спосіб представлення графів має на увазі, що для кожної вершини буде зазначений список всіх суміжних з нею вершин (для орграфа – список вершин, що є кінцями вихідних дуг). Конкретний формат вхідного файлу, що містить списки суміжності, необхідно обговорювати окремо. Наприклад, можна використати наступний формат – початкова вершина відділена від списку суміжності двокрапкою: <номер_початкової_вершини>: <номери_суміжних_вершин> Найбільш природно застосовувати цей спосіб для представлення орграфів, однак і для інших варіантів він теж підходить.
При пошуку в ширину, після відвідування першої вершини, відвідуються всі сусідні з нею вершини. Потім відвідуються всі вершини, що перебувають на відстані двох ребер від початкової. При кожному новому кроці відвідуються вершини, відстань від яких до початкової на одиницю більша попередньої. Щоб запобігти повторному відвідуванню вершин, необхідно вести список відвіданих вершин. Для зберігання тимчасових даних, необхідних для роботи алгоритму, використовується черга – упорядкована послідовність елементів, у якій нові елементи додаються в кінець, а старі видаляються із початку.
Таким чином, основна ідея пошуку в ширину полягає в тому, що спочатку досліджуються всі вершини, суміжні з початковою вершиною (вершина з якої починається обхід). Ці вершини перебувають на відстані 1 від початкової. Потім досліджуються всі вершини на відстані 2 від початкової, потім всі на відстані 3 і так далі. При цьому для кожної вершини відразу визначається довжина найкоротшого маршруту від початкової вершини.
Алгоритм пошуку в ширину
Крок 1. Всім вершинам графа привласнюється значення не відвідана. Вибирається перша вершина й позначається як відвідана (і заноситься в чергу).
Крок 2. Відвідується перша вершина із черги (якщо вона не позначена як відвідана). Всі її сусідні вершини заносяться в чергу. Після цього вона видаляється із черги.
Крок 3. Повторюється крок 2 доти, поки черга не є порожньою (рис. 14.2).
Складність пошуку в ширину при нематричному представленні графа дорівнює O(n + m), тому що розглядаються всі n вершин і m ребер. Використання матриці суміжності приводить до оцінки O(n2).
Якщо задано граф G = (V, E) та початкову вершину s, алгоритм пошуку в ширину систематично обходить всі досяжні із s вершини. На першому кроці вершина s позначається, як пройдена, а в список додаються всі вершини, досяжні з s без відвідування проміжних вершин. На кожному наступному кроці всі поточні вершини списку відмічаються, як пройдені, а новий список формується із вершин, котрі є ще не пройденими сусідами поточних вершин списку. Для реалізації списку вершин найчастіше використовується черга та принцип FIFO. Виконання алгоритму продовжується до досягнення шуканої вершини або до того часу, коли на певному кроці в список не включається жодна вершина. Другий випадок означає, що всі вершини, доступні з початкової, уже відмічені, як пройдені, а шлях до цільової вершини не знайдений.
Алгоритм має назву пошуку в ширину, оскільки «фронт» пошуку (між пройденими та непройденими вершинами) одноманітно розширюється вздовж всієї своєї ширини. Тобто, алгоритм проходить всі вершини на відстані k перед тим як пройти вершини на відстані k+1.
Кроки алгоритму:
Почати з довільної вершини v. Виконати BFS(v):=1. Включити вершину v у чергу.
Розглянути вершину, яка перебуває на початку черги; нехай це буде вершина х. Якщо для всіх вершин, суміжних із вершиною х, уже визначено BFS-номери, то перейти до кроку 4, інакше - до кроку 3.
Нехай {x,y} - ребро, у якому номер BFS(у) не визначено. Позначити це ребро потовщеною суцільною лінією, визначити BFS(у) як черговий BFS-номер, включити вершину у чергу й перейти до кроку 2.
Виключити вершину х із черги. Якщо черга порожня, зупинитись, інакше перейти до кроку 2.
Для початку роботи алгоритму пошуку в ширину необхідно створити чергу, до якої будуть послідовно додаватися вже пройдені вершини, також для того щоб знати шлях повернення, якщо були пройдені всі вершини графа з однієї сторони, потрібно створити масив попередників. В ньому будуть позначатись шляхи, до вершини в яку ми потрапили. Основна ідея алгоритму полягає в обході вершин по кроку, тобто для кожної вершини шукається нащадок, якщо він є, відбувається перехід до нього, якщо немає і задача пошуку не була виконана, алгоритм повертається на попередню вершину.
При пошуку в глибину відвідується перша вершина, потім необхідно йти уздовж ребер графа, до потрапляння в глухий кут. Вершина графа є глухим кутом, якщо всі суміжні з нею вершини вже відвідані. Після влучення в глухий кут потрібно вертатися назад уздовж пройденого шляху, поки не буде виявлена вершина, у якої є суміжна ще не відвідана вершина, а потім необхідно рухатися в цьому новому напрямку. Процес є завершеним при поверненні в початкову вершину, причому всі суміжні з нею вершини вже повинні бути відвідані.
Таким чином, основна ідея пошуку в глибину – коли можливі шляхи по ребрах, що виходять із вершин, розгалужуються, потрібно спочатку повністю досліджувати одну гілку й тільки потім переходити до інших гілок (якщо вони залишаться нерозглянутими).
Алгоритм пошуку в глибину
Крок 1. Всім вершинам графа привласнюється значення «не відвідана». Вибирається перша вершина й позначається як відвідана.
Крок 2. Для останньої позначеної як відвідана вершини вибирається суміжна вершина, що є першою позначеною як не відвідана, і їй привласнюється значення відвідана. Якщо таких вершин немає, то береться попередня позначена вершина.
Крок 3. Повторити крок 2 доти, поки всі вершини не будуть позначені як відвідані.
Алгоритм пошуку в глибину (англ. Depth-first search, DFS) — алгоритм для обходу дерева, структури подібної до дерева, або графу. Робота алгоритму починається з кореня дерева (або іншої обраної вершини в графі) і здійснюється обхід в максимально можливу глибину до переходу на наступну вершину.
Нехай G=(V, E) — простий зв'язний граф, усі вершини якого позначено попарно різними символами. У процесі пошуку вглиб вершинами графу G надають номери (DFS-номери) та певним способом даних для збереження множин, яку називають стеком. Зі стеку можна вилучити тільки той елемент, котрий було додано до нього останнім: стек працює за принципом «останній прийшов — перший вийшов» (LIFO). Інакше кажучи, додавання й вилучення елементів у стеку відбувається з одного кінця, який називається верхівкою стеку. DFS-номери вершини х позначають DFS(х).
Наведемо кроки алгоритму
Почати з довільної вершини v. Виконати DFS(v):=1. Включити цю вершину в стек.
Розглянути вершину у верхівці стеку: нехай це вершина х. Якщо всі ребра, інцидентні вершині х, позначено, то перейти до кроку 4, інакше — до кроку 3.
Нехай {x, y} — непозначене ребро. Якщо DFS(у) уже визначено, то позначити ребро {x, y} штриховою лінією та перейти до кроку 2. Якщо DFS(у) не визначено, то позначити ребро {x, y} потовщеною суцільною лінією, визначити DFS(у) як черговий DFS-номер, включити цю вершину в стек і перейти до кроку 2.
Виключити вершину х зі стеку. Якщо стек порожній, то зупинитись, інакше — перейти до кроку 2.
Обчислювальною складністю алгоритму (або просто складністю) назвемо кількість кроків виконуваних алгоритмом в гіршому випадку. Вона є функцією від розмірності задачі, представленої вхідними даними. Наприклад, для графу, що задається списками інцидентності, розмірність задачі представляється як пара (n, m). Складність алгоритму визначається, як функція f, така, що f (n, m) дорівнює числу кроків алгоритму для довільного графу з n вершинами і m ребрами. Під кроком алгоритму розуміється машинна команда, і при такому визначенні кроку обчислювальна складність залежить від конкретної системи команд і способу трансляції. Нас же буде надалі цікавити не точна складність алгоритму, обчислення якої практично неможливо, а асимптотична складність, яка визначається швидкістю росту числа кроків алгоритму при необмеженому збільшенні розмірності задачі. Крім того, обчислювальна складність алгоритму, обчислена при різних системах команд або способах трансляції, відрізняються один від одного в p разів, де p — речова константа, а їх швидкість росту однакова. Для порівняння швидкості росту двох функцій f(n) i g(n) будемо використовувати позначення f(n) = O[g(n)] або f(n) = Ω[g(n)]. Будемо говорити, що функція f(n) має порядок зростання не більше, ніж функція g(n), що позначається f(n) = O[g(n)], тоді і тільки тоді, коли існують C = const і N > 0, такі, що |f(n)| <= C|g(n)| n>= N Будемо говорити, що функція f(n) має порядок зростання не менше, ніж функція g(n), що позначається f(n) = Ω[g(n)], тоді і тільки тоді, коли існують C = const і N > 0, такі, що |f(n)| >= C|g(n)| n>= N Оцінимо обчислювальну складність рекурсивного варіанту алгоритму пошуку у глибину. O(n) + O(2m) = O(n) + O(m) = O(n+m).
Алгоритм пошуку в глибину. Пошук в глибину є схожим на пошук в ширину, основною відмінністю в цих двох алгоритмах є те, що пошук в глибину спочатку проходить від початкової вершини графа до максимального нащадка. Потім, коли алгоритм дійшов до кінцевої вершини, він аналізує, чи є ще невідомі вершини, якщо такі вершини присутні, алгоритм повертається назад, доки не будуть відомі нові шляхи вниз.
В теорії графів, компонента зв'язності неорієнтованого графа це підграф, в якому будь-які дві вершини зв'язані одна з одною шляхами, і вони не зв'язані з ніякими додатковими вершинами. Наприклад, граф на малюнку праворуч має три компоненти зв'язності. Зв'язний граф має рівно одну компоненту зв'язності, яка містить весь граф.
Для пошуку компонент зв’язності використовується звичайний DFS практично без модифікацій (можна використовувати і BFS) . Під час запуску обходу з однієї вершини, він гарантовано відвідає всі вершини, до яких можливо дістатися, тобто, всю компоненту зв’язності, до якої належить початкова вершина.
Компоненти сильної зв’язності в графі можна знайти за допомогою пошуку в глибину в 3 етапи: Побудувати граф зі зворотними (інвертованими) ребрами Виконати в пошук у глибину і знайти – час закінчення обробки вершини Виконати пошук у глибину в , перебираючи вершини в зовнішньому циклі в порядку убування.
Кількість компонент зв’язності дорівнює числу множин у системі непересічних множин. Кількість множин дорівнює числу представників, а саме кількості таких v, для яких parent[v] = v. У прикладі є три представники: 3, 5 і 6. Тобто в графі є три компоненти зв’язності.
Кістякове дерево (англ. Spanning tree) зв'язаного неорієнтованого графа — ациклічний (тобто, без циклів) зв'язний підграф цього графа, який містить усі його вершини. Неформально кажучи, кістякове дерево складається з деякої підмножини ребер графа, таких, що рухаючись цими ребрами можна з будь-якої вершини графа потрапити до будь-якої іншої.
Кістякове дерево також називають каркасним деревом, покриваючим деревом, кістяком або каркасом графа.
У даній статті розглядається задача побудови мінімального кістякового дерева для неорієнтованого зваженого графа. Нагадаємо, що кістяк – це таке дерево, яке є максимальним по включенню ребер підграфом, що не має циклів, і в якому сума довжин ребер мінімальна. Якщо вихідний граф зв’язний, то буде побудовано кістяк, якщо ж у вихідному графі кілька незв’язних компонент, то результатом буде кістяковий ліс.
Задача про знаходження мінімального кістякового дерева виникає при проектуванні ліній електропередач, трубопроводів, доріг і тому подібне, коли потрібно задані центри з’єднати деякою системою каналів зв’язку так, щоб будь-які два центри були пов’язані або каналом що їх з’єднює, або через інші центри і канали, і щоб загальна довжина (або, наприклад, вартість) каналів зв’язку була мінімальною. У цій ситуації задані центри можна вважати вершинами графа з вагами ребер, рівними довжинам (вартостям) каналів, що з’єднують ці центри. Тоді шукана мережу буде кістяковим деревом найменшої довжини (вартості).
Оскільки граф з вершинами містить різних кістякових дерев, то рішення цієї задачі «сліпим» перебором варіантів вимагало б надзвичайно великих обчислень навіть при відносно малих . Однак для її рішення існують більш ефективні алгоритми, серед яких найвідомішими є описаний нижче алгоритм Прима, алгоритм Крускала та алгоритм Борувки.
Отже, алгоритм Прима відноситься до так званих «жадібних» алгоритмів. Жадібні алгоритми діють, використовуючи, в кожен момент, лише частину результатних даних і беручи краще рішення на основі цієї частини. У нашому випадку, на кожному кроці розглядається множина ребер графа, що допускають приєднання до вже побудованої частини кістякового дерева, і вибирається серед них ребро з найменшою довжиною. Повторюючи цю процедуру, отримують кістякове дерево найменшої довжини.
Робота алгоритму починається з довільної вершини і включення її в кістяк. Для графа, зображеного на рисунку вище, в якості початкової була обрана вершина . На наступному кроці, з усіх вершин, поєднаних з даною, шукається вершина, з’єднана ребром з найменшою довжиною. Це ребро разом з новою вершиною додається в дерево. Після цього, серед вершин, що не увійшли в дерево, шукається вершинуа, з’єднана з уже побудованою частиною кістякового дерева ребром з найменшою довжиною. Це ребро разом з новою вершиною додається в дерево і так далі.
Зазначимо, що ітераційний процес алгоритму Прима продовжується до тих пір, поки в кістяк не потраплять всі вершини розглядуваного графа (або, що те ж саме, ребро). В результаті буде побудований кістяк, який є мінімальним.
Пошук шляху (англ. Pathfinding) — це побудова найкоротшого шляху між двома точками за допомогою комп'ютерної програми. Це практичніший варіант розв'язування лабіринтів. Ця галузь досліджень ґрунтується на алгоритмі Дейкстри для пошуку найкоротшого шляху на зваженому графі.
Задача пошуку шляху тісно пов'язана з задачею про найкоротший шлях у рамках теорії графів, яка розглядає визначення шляху, що найкраще відповідає деяким критеріям (найкоротший, найдешевший, найшвидший і так далі) між двома точками у великій мережі.
Алгоритм Дейкстри — алгоритм на графах, відкритий Дейкстрою. Знаходить найкоротший шлях від однієї вершини графу до всіх інших вершин. Класичний алгоритм Дейкстри працює тільки для графів без ребер від'ємної довжини.
Алгоритм Дейкстри. Дозволяє знаходити найбільш оптимальні шляхи в графі, в якому спочатку вибирається початкова вершина, а шлях до всіх інших є невідомим. Головним недоліком даного алгоритму є те, що він працює тільки для графів в яких всі ребра мають тільки позитивне значення. В житті бувають випадки, коли певні шляхи для компанії не є такими, що дають прибуток (звичайно від таких шляхів необхідно відмовитись, але якщо це шляхи підвищеної важливості, немає вибору і доводиться користуватися ними), то в такому випадку алгоритм Дейкстри не може бути використаним. Алгоритм Дейкстри чудово підходить для вирішення задач прокладання шляху, так як його використання має на увазі граф, який є зваженим (тобто для кожного ребра встановлюється вага, наприклад, платні автомагістралі) та орієнтованим (кожне ребро має направлення). В процесі роботи алгоритму необхідно використовувати три множини: V1 – множина вершин, до яких відстань вже була підрахована, V2 – множина вершин, до яких відстань ще рахується, V3 – множина вершин, відстань до яких ще не рахувалась.
Алгоритм Белмана-Форда. Алгоритм Белмана-Форда є подібним до алгоритму Дейкстри, основною перевагою даного алгоритму є можливість роботи з ребрами, які мають від’ємні ваги. В своїй роботі алгоритм Белмана-Форда використовує метод динамічного програмування (основна задача, розбивається на підзадачі, де для кожної підзадачі шукається вирішення, потім результат підзадач переходить на всю основну задачу.
Динамічний алгоритм, використовується для пошуку найбільш оптимальних шляхів серед всіх вершин в зваженому орієнтованому графі. Коректно працює тільки в випадку, якщо в графі немає циклів з від’ємною вагою, якщо в графі такий цикл все ж присутній, то дозволяє знайти хоча б один від’ємний цикл. Час роботи алгоритму O (n^3), використання пам’яті O(n^2). Алгоритм Флойда-Уоршела є більш загальним в порівнянні з алгоритмом Дейкстри, так як він дозволяє працювати з графами з від’ємними циклами та може знаходити ці цикли під час роботи.
Алгоритм пошуку A* розв'язує задачу для однієї пари із використанням евристики в спробі пришвидшити пошук. Він належить до евристичних алгоритмів пошуку. Використовується для пошуку найкоротшого шляху між двома вершинами графу з додатніми вагами ребер. Описаний 1968 р. Пітером Хартом, Нільсом Нільсоном та Бертрамом Рафаелєм. Алгоритм використовує допоміжну функцію (евристику), аби скеровувати напрям пошуку та скорочувати його тривалість. Алгоритм повний в тому сенсі, що завжди знаходить оптимальний розв'язок.
Принцип дії. Алгоритм ділить вершини на три класи:
невідомі вершини: ці вершини ще не були знайдені. Ще не відомий шлях до них. На початку роботи алгоритму всі вершини, окрім початкової, належать до класу невідомих.
відомі вершини (OpenList): вже відомий (можливо не оптимальний) шлях до цих вершин. Всі відомі вершини разом зі значеннями f {\displaystyle f} зберігаються в списку. З цього списку вибираються, в першу чергу, найперспективніші вершини. Конкретна реалізація цього списку має суттєвий вплив на швидкодію алгоритму, і зазвичай має вигляд черги з пріоритетом (наприклад, бінарна купа). На початку роботи алгоритму до відомих вершин належить лише початкова вершина.
повністю досліджені вершини (ClosedList): до цих вершин вже відомий найкоротший шлях. Повністю досліджені вершини додаються до так званого замкненого списку, аби запобігти багаторазовому дослідженню вже досліджених вершин. Список повністю досліджених вершин на початку роботи алгоритму порожній.
Кожна відома або повністю досліджена вершина має вказівник на попередні вершини. Завдяки цьому вказівникові, можна пройти шляхом від цієї до початкової вершини.
Відтворений за зворотніми вказівниками знайдений шлях починається з кінцевої вершини та прямує до початкової. Аби одразу отримати шлях в правильному напрямі, з початкової вершини до кінцевої, в умовах задачі треба переставити місцями початок та кінець. Якщо шукати шлях починаючи з кінцевої вершини, відтворений список починатиметься з початкової вершини й прямуватиме до кінцевої.
Алгоритм пошуку А* знаходить оптимальний шлях між двома вершинами в графі. В залежності від функції вартості, яка задає кожному ребру його «вагу», оптимальність може означати найкоротший, найшвидший або навіть найпростіший шлях. Теоретично, алгоритм може розв'язувати всі задачі, які можна представити у вигляді задачі пошуку оптимального шляху на графі. Обмеження алгоритму А* описані в розділі Недоліки. Алгоритм A* використовується як для планування шляхів, так і в комп'ютерних іграх. Для планування шляхів, як евристична функція використовується лінійна відстань до цілі, оскільки згідно з нерівністю трикутника вона дає оптимальні оцінки. Також алгоритм А* використовується в іграх, в яких необхідно досягти наперед заданий стан, наприклад, в задачі про вісім ферзів, або в п'ятнашках. Евристикою може слугувати, наприклад, кількість невірно пересунутих камінців.