🕸️ Мережі · Графові алгоритми
📅 Липень 2026⏱ 11 хв🟢 Початковий–середній рівень · Останнє оновлення: 9 липня 2026 р.

Алгоритм Флойда-Уоршелла: найкоротші шляхи між усіма парами за O(n³)

Алгоритм Дейкстри знаходить найкоротший шлях з однієї вершини до всіх інших. Але що робити, якщо потрібна найкоротша відстань між кожною парою вершин одразу — від кожного міста до кожного іншого, від кожного маршрутизатора до кожного іншого? Запуск Дейкстри n разів працює, але алгоритм Флойда-Уоршелла розв'язує всю задачу для всіх пар одразу за допомогою трьох вкладених циклів та одного з найелегантніших рекурентних співвідношень динамічного програмування в інформатиці.

1. Задача про найкоротші шляхи між усіма парами

Маємо зважений орієнтований граф G = (V, E) з n = |V| вершинами та функцією ваги ребер w(u, v) (ваги можуть бути від'ємними, доки не існує циклу з від'ємною сумарною вагою). Задача найкоротших шляхів між усіма парами (APSP) вимагає знайти відстань найкоротшого шляху d(i, j) для кожної впорядкованої пари вершин (i, j).

Наївний підхід — запустити алгоритм з однією джерельною вершиною, як-от Беллмана-Форда, з кожної вершини — коштує O(V² · E) з Беллманом-Фордом, оскільки має толерувати від'ємні ваги. Флойд-Уоршелл розв'язує ту саму задачу, включно з від'ємними вагами, за плоску O(V³) незалежно від кількості ребер графа, що робить його найпростішим коректним вибором для щільних графів.

2. Формулювання через динамічне програмування

Трюк, відкритий незалежно Робертом Флойдом і Стівеном Уоршеллом у 1962 році (спираючись на споріднену ідею Бернара Руа 1959 року), полягає в тому, щоб думати про шляхи не за їх довжиною, а за тим, які проміжні вершини їм дозволено відвідувати.

Визначимо dist[k][i][j] як довжину найкоротшого шляху від i до j, що використовує як проміжні зупинки лише вершини з множини {1, 2, …, k} (самі i та j завжди дозволені як кінцеві точки, незалежно від k).

Базовий випадок (k = 0, проміжні вершини не дозволені): dist[0][i][j] = w(i, j) якщо ребро (i,j) існує dist[0][i][j] = 0 якщо i = j dist[0][i][j] = ∞ інакше Рекурентне співвідношення (дозволяємо вершину k як нову зупинку): dist[k][i][j] = min( dist[k-1][i][j], dist[k-1][i][k] + dist[k-1][k][j] ) Тобто: найкращий шлях, що використовує {1..k}, або (а) взагалі не використовує вершину k — так само, як раніше, або (б) проходить через k рівно один раз — розбивається через k. Відповідь: dist[n][i][j] для кожної пари (i, j).

Оскільки dist[k][·][·] залежить лише від dist[k-1][·][·], всю тривимірну таблицю можна згорнути в одну матрицю n×n, що оновлюється на місці — саме ця "матриця" і стоїть за компактною реалізацією алгоритму.

3. Алгоритм і псевдокод

FLOYD-WARSHALL(W):  // W = матриця ваг n×n, W[i][i] = 0, W[i][j] = ∞ якщо ребра немає
  dist = copy(W)
  next = матриця n×n, next[i][j] = j якщо ребро (i,j) існує інакше null   // для відновлення шляху

  для k = 1 до n:            // дозволена проміжна вершина
    для i = 1 до n:
      для j = 1 до n:
        якщо dist[i][k] + dist[k][j] < dist[i][j]:
          dist[i][j] = dist[i][k] + dist[k][j]
          next[i][j] = next[i][k]     // маршрут через k

  повернути dist, next

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

Порядок циклів має значення: k повинен бути найзовнішнішим циклом. Рекурентне співвідношення покладається на те, що dist[k-1][·][·] повністю фіналізовано перед використанням як проміжної зупинки для наступного k — зміна порядку циклів тихо дає некоректні результати, які можуть виглядати правдоподібно на малих тестових графах.

4. Коректність: індукція за проміжними вершинами

Доведення — це чітка індукція за k, розміром множини дозволених проміжних вершин:

5. Складність

ПоказникЗначенняПримітки
ЧасO(V³)Три вкладені цикли, стала робота всередині
Пам'ятьO(V²)Одна матриця відстаней (оновлення на місці)
Пам'ять із відновленням шляхуO(V²)Додаткова матриця "next", той самий асимптотичний порядок

Оскільки час виконання не залежить від кількості ребер, Флойд-Уоршелл найкраще підходить для щільних графів (E близько до V²), де він відповідає або переважає запуск Дейкстри V разів. На розріджених графах запуск Дейкстри на основі купи з кожної вершини або спеціалізований алгоритм Джонсона асимптотично швидший — див. порівняльну таблицю в кінці.

6. Виявлення від'ємних циклів

На відміну від Дейкстри, Флойд-Уоршелл коректно обробляє від'ємні ваги ребер — доки в графі немає циклу з від'ємною вагою (циклу, чия сумарна вага менша за нуль, що зробило б "найкоротший шлях" невизначеним, оскільки можна нескінченно кружляти навколо нього, щоб необмежено зменшувати загальну вартість).

Виявлення від'ємного циклу (безкоштовний побічний ефект): Після запуску Флойда-Уоршелла перевірте діагональ: якщо dist[i][i] < 0 для будь-якої вершини i: → граф містить цикл із від'ємною вагою, досяжний з i та повертається до i Чому це працює: dist[i][i] починається з 0 (порожній шлях). Він може стати від'ємним лише якщо якась послідовність ребер утворює цикл через i із сумарною від'ємною вагою — саме визначення від'ємного циклу.

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

7. Відновлення шляху

Сама лише матриця відстаней не каже, які вершини відвідує найкоротший шлях. Стандартне рішення — підтримувати паралельну матрицю next[i][j] (як показано в псевдокоді вище), що записує "наступну вершину, до якої йти на найкоротшому шляху від i до j":

RECONSTRUCT-PATH(next, i, j):
  якщо next[i][j] == null: повернути []       // шляху не існує
  path = [i]
  поки i != j:
    i = next[i][j]
    path.push(i)
  повернути path

Кожне оновлення next[i][j] = next[i][k] в основному циклі просто каже: "якщо тепер швидше маршрутизувати i→j через k, то перший крок з i має бути тим самим, яким уже був перший крок з i до k" — невелика, але суттєва деталь, яку багато наївних реалізацій забувають.

8. Флойд-Уоршелл проти повторної Дейкстри та алгоритму Джонсона

МетодЧасова складністьВід'ємні ваги?Найкраще для
Флойд-УоршеллO(V³)Так (без від'ємних циклів)Щільні графи, простота, перевірка від'ємного циклу
Дейкстра × V (купа)O(V·E log V)НіРозріджені графи, всі невід'ємні ваги
Беллман-Форд × VO(V²·E)ТакРідко — поступається Флойду-Уоршеллу на щільних, Джонсону на розріджених
Алгоритм ДжонсонаO(V²log V + VE)Так (без від'ємних циклів)Розріджені графи з від'ємними вагами

Алгоритм Джонсона поєднує переваги обох підходів на розріджених графах: він запускає один прохід Беллмана-Форда, щоб обчислити потенціали вершин, які перезважують кожне ребро до невід'ємного значення, не змінюючи, які шляхи найкоротші, а потім запускає Дейкстру з кожної вершини на перезваженому графі. Для щільних графів (E ≈ V²) член V·E log V алгоритму Джонсона все одно наближається до V³ log V, тому звичайний Флойд-Уоршелл з O(V³) — без накладних витрат на перезважування та з набагато простішим кодом — зазвичай перемагає на практиці до кількох сотень вершин, що й пояснює, чому він залишається стандартним вибором, який викладають першим і використовують найчастіше для графів помірного розміру, як-от дорожні мережі, ігрові карти та невеликі й середні інфраструктурні топології.