Алгоритм Прима - как построить остовное дерево по шагам

Алгоритм Прима строит минимальное остовное дерево (МОД) связного взвешенного неориентированного графа - то есть выбирает такое подмножество рёбер, которое соединяет все вершины и имеет наименьшую суммарную стоимость. Это классическая задача дискретной математики: проектирование сетей, кластеризация, аппроксимация задачи коммивояжёра. В отличие от алгоритма Краскала, который растит лес из отдельных компонент, алгоритм Прима выращивает одно дерево из одной стартовой вершины, на каждом шаге присоединяя ближайшую ещё не включённую вершину.
Что такое минимальное остовное дерево
Пусть дан связный неориентированный граф с весовой функцией . Остовное дерево - это подграф, который содержит все вершины , является деревом (связен и без циклов) и потому имеет ровно ребро. Минимальное остовное дерево - остовное дерево с минимальной суммой весов .
Если веса всех рёбер различны, МОД единственно. Если есть равные веса, минимальных деревьев может быть несколько, но их суммарная стоимость одинакова. Именно на этом свойстве и держатся жадные алгоритмы построения МОД: алгоритм Прима и алгоритм Краскала.
Идея алгоритма Прима
Алгоритм Прима поддерживает множество уже включённых в дерево вершин. На старте для произвольной стартовой вершины . Затем многократно повторяется один шаг: среди всех рёбер, у которых один конец лежит в , а другой вне , выбирается ребро минимального веса, и его внешний конец добавляется в . Процесс заканчивается, когда .
Такие «пограничные» рёбра между и образуют разрез. Ключевое утверждение - лемма о разрезе: для любого разреза самое лёгкое пересекающее его ребро принадлежит некоторому минимальному остовному дереву. Алгоритм Прима на каждом шаге как раз и берёт минимальное ребро текущего разреза, поэтому он корректен.
Хотите прогнать алгоритм Прима на собственном графе и увидеть, какое ребро выбирается на каждом шаге? Опишите рёбра ниже - инструмент соберёт пошаговую трассировку с растущим множеством и итоговой стоимостью дерева.
Псевдокод
В наивной реализации на каждой вершине поддерживается величина - минимальный вес ребра, соединяющего с уже построенным деревом, и - родитель в дереве.
для всех v из V:
key[v] = +inf
pi[v] = nil
key[r] = 0
Q = V // приоритетная очередь по key
пока Q не пуста:
u = extract-min(Q) // вершина с минимальным key
для каждого ребра (u, v) с весом w:
если v в Q и w < key[v]:
key[v] = w
pi[v] = u
decrease-key(v)
После завершения рёбра для всех образуют минимальное остовное дерево. Обратите внимание: хранит вес единственного ребра, а не накопленное расстояние, - это и отличает алгоритм Прима от алгоритма Дейкстры, где обновляется длина всего пути.
Пошаговый разбор на примере
Рассмотрим граф из 5 вершин со следующими рёбрами и весами: , , , , , . Стартуем с вершины .
- . Пограничные рёбра: , . Минимум - , добавляем .
- . Рёбра разреза: , , . Минимум - , добавляем .
- . Рёбра: , . Минимум - , добавляем .
- . Рёбра: , . Минимум - , добавляем .
Все вершины включены. Дерево состоит из рёбер суммарным весом . Заметьте, что более дешёвое на вид ребро в дерево не вошло: к моменту рассмотрения уже была достижима через .
Сложность и выбор структуры данных
Время работы целиком определяется тем, как реализована операция extract-min и decrease-key.
- Без приоритетной очереди (поиск минимума простым перебором массива): . Хорош для плотных графов, где близко к .
- Двоичная куча: . извлечений минимума и до операций decrease-key, каждая за .
- Фибоначчиева куча: амортизированное - теоретический оптимум, но на практике константа велика и куча почти не используется.
Для разреженных графов выгодна двоичная куча, для плотных - простой массив на . Это зеркально отражает выбор структуры в алгоритме Дейкстры - см. разбор алгоритма Дейкстры на взвешенном графе, где приоритетная очередь устроена аналогично.
Прим против Краскала
Оба алгоритма жадные и оба строят корректное МОД, но растят его по-разному. Алгоритм Краскала сортирует все рёбра по весу и добавляет их по очереди, пропуская те, что образуют цикл (проверка через систему непересекающихся множеств). Алгоритм Прима растит единое дерево из стартовой вершины, не сортируя рёбра глобально.
Практическое правило: на плотных графах быстрее Прим с массивом (), на разреженных - Краскал ( на сортировку) или Прим с кучей. Если граф уже задан списком отсортированных рёбер, естественнее Краскал. Если граф плотный и хранится матрицей смежности - Прим. По смежной логике разрезов и циклов полезно сравнить с задачами на радиус и диаметр графа.
Частые ошибки
- Путают с расстоянием. В алгоритме Прима - вес одного ребра до дерева, а не длина пути от старта. Если случайно накапливать сумму, получится не МОД.
- Применяют к ориентированному графу. МОД определено для неориентированных графов. Для ориентированных аналог - оптимальное входящее дерево (алгоритм Эдмондса), это другая задача.
- Забывают про связность. Если граф несвязен, остовного дерева не существует - алгоритм Прима построит дерево только для компоненты стартовой вершины. Нужен остовный лес.
- Считают, что отрицательные веса ломают алгоритм. В отличие от Дейкстры, алгоритм Прима корректно работает с отрицательными весами рёбер: лемма о разрезе от знака весов не зависит.
- Останавливаются по числу рёбер неверно. Дерево готово, когда добавлено ровно ребро или , а не когда «кончились дешёвые рёбра».
FAQ
Чем алгоритм Прима отличается от алгоритма Дейкстры? Структурно они почти идентичны (приоритетная очередь, релаксация соседей), но минимизируют разное: Прим минимизирует вес присоединяемого ребра (), Дейкстра - длину всего пути от источника ().
Зависит ли результат от выбора стартовой вершины? Сумма весов МОД - нет, она всегда минимальна и одинакова. Конкретный набор рёбер может отличаться, только если в графе есть рёбра равного веса.
Работает ли алгоритм Прима с отрицательными весами? Да. Лемма о разрезе не использует положительности весов, поэтому отрицательные рёбра обрабатываются корректно. Это отличает его от алгоритма Дейкстры.
Коротко
Алгоритм Прима строит минимальное остовное дерево, выращивая одно дерево из стартовой вершины и на каждом шаге присоединяя ближайшую внешнюю вершину по самому лёгкому ребру разреза. Корректность следует из леммы о разрезе. Сложность - с массивом или с двоичной кучей; выбор структуры зависит от плотности графа. От Краскала отличается стратегией (единое дерево против слияния компонент), от Дейкстры - тем, что минимизирует вес ребра, а не длину пути.
Читайте также

Алгоритм Эдмондса-Карпа: поиск максимального потока
Алгоритм Эдмондса-Карпа находит максимальный поток в сети: BFS ищет дополняющий путь, что даёт полиномиальную сложность. Разбираем идею, реализацию и оценку.

Алгоритм Куна: как найти максимальное паросочетание
Алгоритм Куна шаг за шагом: ищем увеличивающие цепи обычным DFS и находим максимальное паросочетание в двудольном графе за O(V·E), с разбором идеи и сложности.

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