Минимальное покрывающее дерево графа

Алгоритм Краскала

Алгоритм Краскала (англ. Kruskal’s algorithm) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе.

Идея

Будем последовательно строить подграф [math]F[/math] графа [math]G[/math] («растущий лес»), пытаясь на каждом шаге достроить [math]F[/math] до некоторого MST. Начнем с того, что включим в [math]F[/math] все вершины графа [math]G[/math] . Теперь будем обходить множество [math]E(G)[/math] в порядке неубывания весов ребер. Если очередное ребро [math]e[/math] соединяет вершины одной компоненты связности [math]F[/math] , то добавление его в остов приведет к возникновению цикла в этой компоненте связности. В таком случае, очевидно, [math]e[/math] не может быть включено в [math]F[/math] . Иначе [math]e[/math] соединяет разные компоненты связности [math]F[/math] , тогда существует [math] \langle S, T \rangle [/math] разрез такой, что одна из компонент связности составляет одну его часть, а оставшаяся часть графа — вторую. Тогда [math]e[/math] — минимальное ребро, пересекающее этот разрез. Значит, из леммы о безопасном ребре следует, что [math]e[/math] является безопасным, поэтому добавим это ребро в [math]F[/math] . На последнем шаге ребро соединит две оставшиеся компоненты связности, полученный подграф будет минимальным остовным деревом графа [math]G[/math] . Для проверки возможности добавления ребра используется система непересекающихся множеств.

Реализация

// [math]G[/math] — исходный граф // [math]F[/math] — минимальный остов function [math]\mathtt():[/math] [math] \mathtt \leftarrow V(G)[/math] [math]\mathtt(E(G))\[/math] for [math]vu \in E(G)[/math] if [math]v[/math] и [math]u[/math] в разных компонентах связности [math]F[/math] [math] \mathtt\ =\ \mathtt \bigcup vu\[/math] return [math] \mathtt [/math] 

Задача о максимальном ребре минимального веса

Легко показать, что максимальное ребро в MST минимально. Обратное в общем случае неверно. Но MST из-за сортировки строится за [math]O(E \log E)[/math] . Однако из-за того, что необходимо минимизировать только максимальное ребро, а не сумму всех рёбер, можно предъявить алгоритм, решающий задачу за линейное время.

С помощью алгоритма поиска k-ой порядковой статистики найдем ребро-медиану за [math]O(E)[/math] и разделим множество ребер на два равных по мощности так, чтобы ребра в первом не превосходили по весу ребер во втором. Проверим образуют ли ребра из первого подмножества остов графа, запустив обход в глубину.

  • Если да, то рекурсивно запустим алгоритм от него.
  • В противном случае сконденсируем получившиеся несвязные компоненты в супервершины и рассмотрим граф с этими вершинами и ребрами из второго подмножества.

На последнем шаге останутся две компоненты связности и одно ребро в первом подмножестве — это максимальное ребро минимального веса.

Читайте также:  Пчелы на ветке дерева

На каждом шаге ребер становится в два раза меньше, а все операции выполняются за время пропорциональное количеству ребер на текущем шаге, тогда время работы алгоритма [math]O(E+\frac+\frac+. +1)=O(E)[/math] .

Пример

Рёбра (в порядке их просмотра) ae cd ab be bc ec ed
Веса рёбер [math]1[/math] [math]2[/math] [math]3[/math] [math]4[/math] [math]5[/math] [math]6[/math] [math]7[/math]
Изображение Описание
Первое ребро, которое будет рассмотрено — ae, так как его вес минимальный.

Добавим его к ответу, так как его концы соединяют вершины из разных множеств (a — красное и e — зелёное).
Объединим красное и зелёное множество в одно (красное), так как теперь они соединены ребром.

Добавим его к ответу, так как его концы соединяют вершины из разных множеств (c — синее и d — голубое).
Объединим синее и голубое множество в одно (синее), так как теперь они соединены ребром.

Добавим его к ответу, так как его концы соединяют вершины из разных множеств (a — красное и b — розовое).
Объединим красное и розовое множество в одно (красное), так как теперь они соединены ребром.

Оно соединяет вершины из одного множества, поэтому перейдём к следующему ребру bc
Добавим его к ответу, так как его концы соединяют вершины из разных множеств (b — красное и c — синее).
Объединим красное и синее множество в одно (красное), так как теперь они соединены ребром.

поэтому после их просмотра они не будут добавлены в ответ
Всё рёбра были рассмотрены, поэтому алгоритм завершает работу.
Полученный граф — минимальное остовное дерево

Асимптотика

Сортировка [math]E[/math] займет [math]O(E\log E)[/math] .
Работа с СНМ займет [math]O(E\alpha(V))[/math] , где [math]\alpha[/math] — обратная функция Аккермана, которая не превосходит [math]4[/math] во всех практических приложениях и которую можно принять за константу.
Алгоритм работает за [math]O(E(\log E+\alpha(V))) = O(E\log E)[/math] .

См. также

Источники информации

  • Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн — Алгоритмы: построение и анализ, 2-е издание. Пер. с англ. — М.:Издательский дом «Вильямс», 2010. — 1296 с.: ил. — Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)
  • Википедия — Функция Аккермана
  • Википедия — Алгоритм Крускала
  • Wikipedia — Kruskal’s algorithm
  • MAXimal :: algo :: Минимальное остовное дерево. Алгоритм Крускала

Источник

Алгоритм Прима

Алгоритм Прима (англ. Prim’s algorithm) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе.

Идея

Данный алгоритм очень похож на алгоритм Дейкстры. Будем последовательно строить поддерево [math]F[/math] ответа в графе [math]G[/math] , поддерживая приоритетную очередь [math]Q[/math] из вершин [math]G \setminus F[/math] , в которой ключом для вершины [math]v[/math] является [math]\min\limits_w(uv)[/math] — вес минимального ребра из вершин [math]F[/math] в вершины [math]G \setminus F[/math] . Также для каждой вершины в очереди будем хранить [math]p(v)[/math] — вершину [math]u[/math] , на которой достигается минимум в определении ключа. Дерево [math]F[/math] поддерживается неявно, и его ребра — это пары [math]\left(v,p(v)\right)[/math] , где [math]v \in G \setminus \ \setminus Q[/math] , а [math]r[/math] — корень [math]F[/math] . Изначально [math]F[/math] пусто и значения ключей у всех вершин равны [math]+\infty[/math] . Выберём произвольную вершину [math]r[/math] и присвоим её ключу значение [math]0[/math] . На каждом шаге будем извлекать минимальную вершину [math]v[/math] из приоритетной очереди и релаксировать все ребра [math]vu[/math] , такие что [math]u \in Q[/math] , выполняя при этом операцию [math]\text[/math] над очередью и обновление [math]p(v)[/math] . Ребро [math]\left(v,p(v)\right)[/math] при этом добавляется к ответу.

Реализация

// [math]G[/math] — исходный граф // [math]w[/math] — весовая функция function [math]\mathtt():[/math] for [math]v \in V(G)[/math] [math]\mathtt[v]\ =\ \infty[/math] [math]\mathtt

[v]\ =[/math] null [math]r\ =[/math] произвольная вершина графа [math]G[/math] [math]\mathtt[r]\ =\ \mathtt[/math] [math]Q.\mathtt(V(G))[/math] while not [math]Q.\mathtt[/math] [math]v\ =\ Q.\mathtt()[/math] for [math]vu \in E(G)[/math] if [math]u \in Q[/math] and [math]\mathtt[u] \gt w(v, u)[/math] [math]\mathtt

[u]\ =\ v[/math] [math]\mathtt[u]\ =\ w(v, u)[/math] [math]Q.\mathtt(u, \mathtt[u])[/math]

Ребра дерева восстанавливаются из его неявного вида после выполнения алгоритма.
Чтобы упростить операцию [math]\mathrm[/math] можно написать кучу на основе сбалансированного бинарного дерева поиска. Тогда просто удалим вершину и добавим ее обратно уже с новым ключом. Асимптотика таких преобразований [math]O(\log n)[/math] . Если же делать с бинарной кучей, то вместо операции [math]\mathrm[/math] , будем всегда просто добавлять вершину с новым ключом, если из кучи достали вершину с ключом, значение которого больше чем у нее уже стоит, просто игнорировать. Вершин в куче будет не больше [math]n^2[/math] , следовательно, операция [math]\mathrm[/math] будет выполняться за [math]O(\log n^2)[/math] , что равно [math]O(\log n)[/math] . Максимальное количество вершин, которое мы сможем достать, равняется количеству ребер, то есть [math]m[/math] , поэтому общая асимптотика составит [math]O(m \log n)[/math] , что хорошо только на разреженных графах.

Пример

Рассмотрим работу алгоритма на примере графа. Пусть произвольно выбранная вершина — это вершина a.

Корректность

По поддерживаемым инвариантам после извлечения вершины [math]v\ (v \neq r)[/math] из [math]Q[/math] ребро [math]\left(v,p(v)\right)[/math] является ребром минимального веса, пересекающим разрез [math]\left(F,Q\right)[/math] . Значит, по лемме о безопасном ребре, оно безопасно. Алгоритм построения MST, добавляющий безопасные ребра, причём делающий это ровно [math]|V|-1[/math] раз, корректен.

Оценка производительности

Производительность алгоритма Прима зависит от выбранной реализации приоритетной очереди, как и в алгоритме Дейкстры. Извлечение минимума выполняется [math]V[/math] раз, релаксация — [math]O(E)[/math] раз.

Структура данных для приоритетной очереди Асимптотика времени работы
Наивная реализация [math]O(V^2+E)[/math]
Двоичная куча [math]O(E\log)[/math]
Фибоначчиева куча [math]O(V\log+E)[/math]

См. также

Источники информации

  • Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн — Алгоритмы: построение и анализ, 2-е издание. Пер. с англ. — М.:Издательский дом «Вильямс», 2010. — с.653 — 656.— ISBN 978-5-8459-0857-5 (рус.)
  • Википедия — Алгоритм Прима
  • Wikipedia — Prim’s algorithm
  • MAXimal :: algo :: Минимальное остовное дерево. Алгоритм Прима

Источник

Минимальное остовное дерево. Алгоритм Прима. Алгоритм Крускала

Остовным деревом графа называется дерево, которое можно получить из него путём удаления некоторых рёбер. У графа может существовать несколько остовных деревьев, и чаще всех их достаточно много.

На иллюстрации приведено одно из остовных деревьев (рёбра выделены синим цветом) решёткообразного графа.

Для взвешенных графов существует понятие веса остовного дерева, которое определено как сумма весов всех рёбер, входящих в остовное дерево. Из него натурально вытекает понятие минимального остовного дерева — остовного дерева с минимальным возможным весом.

Для нахождения минимального остовного дерева графа существуют два основных алгоритма: алгоритм Прима и алгоритм Крускала. Они оба имеют сложность \(O(M \log N)\), поэтому выбор одного из них зависит от ваших личных предпочтений. В этой лекции мы разберём оба.

Алгоритм Прима

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

Утверждается, что если разделить вершины графа на два множества (обработанные и необработанные), первое из которых составляет связную часть минимального остовного дерева, то ребро минимальной длины, связывающее эти два множества гарантированно будет входить в минимальное остовное дерево.

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

На иллюстрации красным цветом выделены рёбра, уже вошедшие в минимальный остов, а чёрным — текущие кандидаты, из которых выбирается ребро с минимальным весом.

Реализация алгоритма Прима

Будем искать вес минимального остовного дерева. Для нахождения ближайшей вершины воспользуемся очередью с приоритетом (аналогично алгоритму Дейкстры), в которой будем хранить пары (расстояние от остова до вершины, номер вершины).

Различия в скорости работы

Хотя оба алгоритма работают за \(O(M \log N)\), существуют константные различия в скорости их работы. На разреженных графах (количество рёбер примерно равно количеству вершин) быстрее работает алгоритм Крускала, а на насыщенных (количество рёбер примерно равно квадрату количеству вершин) — алгоритм Прима (при использовании матрицы смежности).

На практике чаще используется алгоритм Крускала.

brestprog

Олимпиадное программирование в Бресте и Беларуси

Источник

Оцените статью