Вопрос с собеседования · Основы

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

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

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

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

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

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

Поиск пути на сетке: 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

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования