5.5. Задача поиска кратчайшего пути в графе
Автор: Мария Горденко
В этой статье вы познакомитесь с алгоритмом Дейкстры — одним из самых известных алгоритмов на графах. Он позволяет находить кратчайшие пути от одной вершины до всех остальных в графе без отрицательных весов. Вы узнаете, как работает идея постепенного уточнения расстояний, научитесь реализовывать алгоритм шаг за шагом и поймёте, как восстанавливается путь.
Ключевые вопросы статьи
- Как устроен алгоритм Дейкстры и зачем нужны веса рёбер?
- Почему алгоритм не работает с отрицательными весами?
- Как восстановить кратчайший путь после завершения алгоритма?
Принцип работы алгоритма Дейкстры
Зачастую в графах требуется находить между вершинами кратчайшие пути. Один из алгоритмов нахождения кратчайших путей от заданной вершины до любой другой — алгоритм Дейкстры. Алгоритм работает только для графов без рёбер отрицательного веса.
Асимптотическая сложность нахождения компонент связности в графе — O(V+E), где V — число вершин, а E — число рёбер и дуг.
Опишем принцип работы алгоритма Дейкстры:
- Шаг 1. Всем вершинам, за исключением первой, присваивается вес равный бесконечности, а первой вершине — 0.
- Шаг 2. Все вершины не посещены.
- Шаг 3. Первая вершина объявляется текущей.
- Шаг 4. Вес всех невыделенных вершин пересчитывается по формуле: вес невыделенной вершины есть минимальное число из старого веса данной вершины, суммы веса текущей вершины и веса ребра, соединяющего текущую вершину с невыделенной.
- Шаг 5. Среди невыделенных вершин ищется вершина с минимальным весом. Если такова не найдена, то есть вес всех вершин равен бесконечности, то маршрута не существует. Следовательно, выход. Иначе, текущей становится найденная вершина. Она же выделяется.
- Шаг 6. Если текущей вершиной оказывается конечная, то путь найден, и его вес есть вес конечной вершины.
- Шаг 7. Переход на шаг 4.
Пример работы алгоритма показан на картинке ниже.
Посмотрим на реализацию.
Dijkstra(graph, start, finish, used):
vector d(n, inf), p(n, -1)
n = len(graph)
graph[v] = 1
for (int i = 0; i < n; ++i)
int v = -1
for (int j = 0; j < n; ++j)
if (!used[j] and (v == -1 or d[j] < d[v]))
v = j
used[v] = true
for (int j = 0; j < len(graph[v]); ++j)
to = graph[v][i].vertex
len = graph[v][i].edge
if (d[v] + len < d[to])
d[to] = d[v] + len
p[to] = v
Асимптотическая сложность алгоритма Дейкстры — , где — число вершин, а — число рёбер и дуг.
Упражнение 1
Подумайте, как восстановить путь, используя введённый массив p?
Упражнение 2
Как изменится кратчайший путь, если все веса рёбер увеличить на какое-то число?
Упражнение 3
Как изменится кратчайший путь, если все веса рёбер увеличить в какое-то число раз?
Упражнение 4
Подумайте, почему алгоритм работает только для графов без рёбер отрицательного веса?
Что дальше
Теперь вы умеете находить кратчайшие пути в графах с помощью алгоритма Дейкстры. Вы научились пошагово уточнять расстояния до вершин, работать с массивом предков и восстанавливать путь. Вы также поняли, в каких случаях алгоритм применим, а в каких — нет.
Далее — завершение раздела. Мы кратко подведём итоги, сравним изученные подходы и обобщим стратегии, которые помогут вам уверенно решать задачи на графы.
Ключевые выводы статьи
- Алгоритм Дейкстры находит кратчайшие пути от одной вершины до всех остальных в графе без отрицательных рёбер.
- Идея алгоритма — постепенно уточнять расстояния, переходя от ближайших вершин к более дальним.
- Для восстановления пути используется массив предков.
- Алгоритм не работает корректно при наличии отрицательных весов — для таких случаев нужны другие подходы (например, Беллмана — Форда).