Menu
Coddy logo textTech

Алгоритм Дейкстры

Последнее обновление

Алгоритм Дейкстры находит кратчайший путь от исходной вершины до всех остальных вершин графа с неотрицательными весами рёбер. Он хранит предварительное расстояние для каждой вершины, многократно фиксирует незафиксированную вершину с наименьшим предварительным расстоянием и релаксирует её рёбра, обновляя расстояние соседа всякий раз, когда находится более короткий маршрут через текущую вершину. Нажмите «воспроизвести» выше, чтобы увидеть, как расстояния уменьшаются по мере фиксации каждой вершины.

Ключевая идея жадная: как только выбрана ближайшая незафиксированная вершина, её расстояние окончательно, потому что любой другой маршрут к ней должен был бы пройти через вершину, которая уже дальше. С очередью с приоритетом на двоичной куче Дейкстра работает за время O((V + E) log V). Он требует неотрицательных весов — используйте Беллмана-Форда, если рёбра могут быть отрицательными.

Временная и пространственная сложность

РеализацияСложностьПримечания
Двоичная кучаO((V + E) log V)Обычный практичный выбор
Просмотр массиваO(V²)Проще; подходит для плотных графов
ПамятьO(V)Расстояния + очередь с приоритетом
ТребуетНеотрицательные весаОтрицательные рёбра ломают жадный выбор

Пошагово

ШагЧто происходит
1Установите расстояние источника в 0, а все остальные — в бесконечность.
2Выберите незафиксированную вершину с наименьшим предварительным расстоянием.
3Отметьте её как зафиксированную — её кратчайшее расстояние теперь окончательно.
4Для каждого соседа вычислите расстояние-через-текущую + вес ребра.
5Если оно меньше текущего расстояния соседа, релаксируйте его.
6Повторяйте, пока все достижимые вершины не будут зафиксированы.

Разобранный пример

Кратчайшие пути от источника A в графе с рёбрами A-B=4, A-C=1, C-B=2, C-D=5, B-D=1:

ШагФиксацияРасстоянияДействие
0-A=0, B=∞, C=∞, D=∞Инициализация: источник A=0, все остальные — бесконечность.
1A (0)B=4, C=1, D=∞Релаксируем рёбра из A: устанавливаем B=4, C=1.
2C (1)B=3, D=6Через C: B=1+2=3 лучше 4; D=1+5=6.
3B (3)D=4Через B: D=3+1=4 лучше 6.
4D (4)A=0, C=1, B=3, D=4Фиксируем D; релаксировать больше нечего. Готово.

Когда использовать алгоритм Дейкстры

Используйте, когдаИзбегайте, когда
Все веса рёбер неотрицательныЛюбое ребро может быть отрицательным — используйте Беллмана-Форда
Нужны кратчайшие пути от одного источника ко всем вершинамНужны кратчайшие пути между всеми парами — Флойд-Уоршелл проще
Граф взвешенный и нужны точные расстоянияГраф невзвешенный — простой BFS быстрее и проще
У вас есть хорошая куча или очередь с приоритетомНужно быстро достичь одной цели с эвристикой — используйте A*

Код Dijkstra's Algorithm

Чистая, готовая к запуску реализация Dijkstra's Algorithm на Python, JavaScript, Java, C++, C. Выберите язык, скопируйте код или откройте его уже загруженным в плейграунде Coddy.

Код Dijkstra's Algorithm на Python

Python
1import heapq2
3
4def dijkstra(graph, start):5    dist = {node: float("inf") for node in graph}6    dist[start] = 07    pq = [(0, start)]8    while pq:9        d, node = heapq.heappop(pq)10        if d > dist[node]:11            continue  # stale entry, a shorter path was already found12        for neighbor, weight in graph[node]:13            new_dist = d + weight14            if new_dist < dist[neighbor]:15                dist[neighbor] = new_dist16                heapq.heappush(pq, (new_dist, neighbor))17    return dist18
19
20graph = {21    "A": [("B", 4), ("C", 1)],22    "B": [("D", 1)],23    "C": [("B", 2), ("D", 5)],24    "D": [("E", 3)],25    "E": [],26}27
28for node, d in dijkstra(graph, "A").items():29    print(f"A -> {node}: {d}")
Запустите этот код в плейграунде Python

Часто задаваемые вопросы об алгоритме Дейкстры

Какова временная сложность алгоритма Дейкстры?
С очередью с приоритетом на двоичной куче он работает за O((V + E) log V). Простая версия, которая на каждом шаге просматривает массив в поиске минимума, имеет сложность O(V²), что на плотных графах может оказаться даже быстрее. Обе используют память O(V).
Почему Дейкстра не работает с отрицательными весами рёбер?
Дейкстра предполагает, что как только зафиксирована ближайшая незафиксированная вершина, это расстояние окончательно. Отрицательное ребро могло бы позже создать более короткий путь к уже зафиксированной вершине, нарушив это предположение. Для графов с отрицательными весами используйте алгоритм Беллмана-Форда.
В чём разница между Дейкстрой и BFS?
BFS находит кратчайшие пути, считая рёбра (вес каждого ребра фактически равен 1), используя обычную очередь. Дейкстра обобщает это на взвешенные графы, всегда расширяя вершину с наименьшим суммарным расстоянием с помощью очереди с приоритетом. На невзвешенном графе оба дают одни и те же пути.
В чём разница между Дейкстрой и поиском A*?
A* — это Дейкстра плюс эвристика, которая оценивает оставшееся расстояние до цели, поэтому она направляет поиск к этой цели вместо равномерного расширения во все стороны. Когда эвристика равна нулю, A* вырождается ровно в Дейкстру. Используйте A*, когда у вас одна цель и хорошая допустимая эвристика; используйте Дейкстру, когда нужны расстояния до всех вершин.
Когда использовать Дейкстру вместо Беллмана-Форда?
Используйте Дейкстру всякий раз, когда все веса рёбер неотрицательны — она быстрее: O((V + E) log V) против O(V·E) у Беллмана-Форда. Выбирайте Беллмана-Форда только тогда, когда рёбра могут быть отрицательными или нужно обнаруживать отрицательные циклы. На неотрицательных графах Дейкстра почти всегда лучший выбор.
Может ли Дейкстра снова посетить вершину после её фиксации?
Нет — как только вершина зафиксирована, её расстояние окончательно и она больше никогда не релаксируется. Частая ошибка в реализациях на куче — оставлять устаревшие записи в очереди с приоритетом после того, как расстояние вершины улучшилось; нужно пропускать извлечённую вершину, если она уже зафиксирована (её извлечённое расстояние превышает записанное). Пропуск этой проверки всё равно даёт правильные ответы, но тратит лишнюю работу.
Coddy programming languages illustration

Освойте алгоритмы с Coddy

НАЧАТЬ