Быстрый алгоритм для минимальных остовных деревьев при ограничении длин ребер?
Предположим, что у вас есть ориентированный граф с неотрицательными целыми длинами ребер, которые находятся в диапазоне от 0 до U - 1 включительно. Каков самый быстрый алгоритм вычисления минимального остовного дерева этого графа? Мы все еще можем использовать существующие алгоритмы минимального остовного дерева, такие как алгоритм Крускаля O (m log n)) или алгоритм Prim (O (m + n log n)). Однако для случаев, когда U мало, я думаю, что это должно быть возможно сделать намного лучше.
Существуют ли какие-либо алгоритмы, конкурирующие с более традиционными алгоритмами MST, которые могут использовать тот факт, что длины кромок ограничены в каком-то диапазоне?
Спасибо!
Ответы
Ответ 1
Фредман-Уиллард дал алгоритм O (m + n) для целых длин ребер на ОЗУ с единичной стоимостью.
Это, возможно, не так много улучшений: без ограничения длины ребер (т.е. длины - это непрозрачный тип данных, который поддерживает только сравнения), Chazelle дал алгоритм O (m alpha (m, n) + n) alpha - обратная функция Аккермана), а Каргер-Клейн-Тарьян дал рандомизированный алгоритм O (m + n).
Я не думаю, что идея Даррена приводит к алгоритму O (m + n + U) -time. Jarnik ( "Prim" ) не использует свою очередь приоритетов монотонно, поэтому ведра могут сканироваться несколько раз; Для Kruskal требуется структура данных с непересекающимися множествами, которая не может быть O (m + n).
Ответ 2
С целыми весами по краю вы можете использовать bucketing для достижения очереди приоритетов с наихудшей сложностью O(1)
, но дополнительной сложности O(U)
.
В описанных выше алгоритмах MST вы должны заменить очереди приоритетов, основанные на сопоставлении, с этой целочисленной структурой и, следовательно, удалить зависимость O(log(n))
в требованиях сложности. Я ожидаю, что вы закончите с общей сложностью в стиле O(n + m)
.
По существу вы настраиваете набор сжатых связанных списков, где каждый список индексируется стоимостью (integer!), связанной с этим ведром:
struct bucket_list
{
_cost; // array[0..N-1] holding current cost for each item
_head; // array[0..U-1] holding index of first item in each bucket
_next; // array[0..N-1] where _next[i] is the next item
// in a list for the ith item
_prev; // array[0..N-1] where _prev[i] is the last item
// in a list for the ith item
};
Эта структура основана на том факте, что каждый элемент может быть только в одном списке сразу.
Основываясь на этой структуре, вы можете достичь сложности худшего случая O(1)
для этих операций:
push(item, cost); // push an item onto the head of the appropriate bucket list
_pop(item, cost); // _pop an item from (anywhere!) within a bucket list
update(item, old_cost, new_cost); // move an item between buckets by combining
// push and _pop
Чтобы использовать эту структуру в качестве очереди приоритетов, вы просто поддерживаете индекс, указывающий на текущее минимальное ведро для сканирования. Когда вы хотите получить следующую вещь с низкой стоимостью, вы просто вытаскиваете головной элемент из этого ведра. Если ведро пустое, вы увеличиваете индекс вашего ведра до тех пор, пока не найдете непустую.
Конечно, если U
становится очень большим, дополнительная сложность пространства и возможность разреженного распределения элементов над ведрами могут сделать такой подход непривлекательным.