Получить доступ

Как работает алгоритм Дейкстры?

MiddleГрафы
Ещё спрашивают так:
  • Как найти кратчайший путь во взвешенном графе?
  • Почему Дейкстра не работает с отрицательными весами?
  • Какая сложность у алгоритма Дейкстры?
Коротко

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

  • Ближайшую вершину достают из очереди с приоритетом (кучи): O((V + E) log V)
  • Когда вершину достали из очереди, расстояние до неё окончательное

А ещё Отрицательные веса ломают это свойство — нужен Беллман — Форд

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

Поиск пути на сетке: BFS, DFS, Дейкстра, A*, жадный

Подробно

Идея. Храним текущие оценки расстояний dist: у старта 0, у остальных бесконечность. Повторяем:

  1. достать из очереди с приоритетом вершину v с наименьшим dist;
  2. для каждого ребра v → u с весом w: если dist[v] + w < dist[u], обновить dist[u] и положить u в очередь (это называется ослаблением ребра);
  3. запоминать родителя, чтобы восстановить путь.

Почему это верно. Все веса неотрицательны, поэтому вершина, у которой сейчас наименьшая оценка, не может получить ещё меньшую через другие вершины — любой обходной путь только длиннее. Значит, в момент извлечения её расстояние окончательное. Это «жадный» шаг.

Отрицательные веса ломают рассуждение: обходной путь через отрицательное ребро может оказаться короче уже «окончательного». Для них используют алгоритм Беллмана — Форда (O(V·E)), он же находит отрицательные циклы.

Сложность с двоичной кучей — O((V + E) log V). Для невзвешенного графа Дейкстра вырождается в BFS. Если нужен путь до одной конкретной цели, можно остановиться, когда цель извлечена из очереди, а ещё лучше — использовать A* с эвристикой.

Применения: маршрутизация в сетях (OSPF), навигация, стоимость перемещения в играх.

Пример

import heapq

def dijkstra(graph, start):
    dist = {start: 0}
    heap = [(0, start)]
    while heap:
        d, v = heapq.heappop(heap)
        if d > dist.get(v, float("inf")):
            continue                      # устаревшая запись в куче
        for u, w in graph[v]:
            nd = d + w
            if nd < dist.get(u, float("inf")):
                dist[u] = nd
                heapq.heappush(heap, (nd, u))
    return dist

Что хотят услышать

  • Жадный выбор ближайшей вершины и ослабление рёбер.
  • Роль очереди с приоритетом и сложность.
  • Почему нельзя отрицательные веса и что вместо.
  • Связь с BFS и A* — плюс.

Частые ошибки

  • Применять к графу с отрицательными весами.
  • Забыть пропускать устаревшие записи в куче.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем