%-----------------------------------------------
Guy Blelloch, 1995
Finds the spanning tree of a graph using the hybrid
technique discussed in:
John Greiner.
A Comparison of Data-Parallel Algorithms for Connected Components.
Proceedings Symposium on Parallel Algorithms and Architectures,
pages 16--25, June 1994.
For m edges and n vertices, it has a worst case work of O(m
lg n) and a worst case time of O(lg^2 n). For many types of
graphs it does closer to O(m) work with O(lg n) time.
In this implementation graphs are represented as a tagged
edgelist. Each edge is represented as a tripple consisting
of a unique identifier for the edge, and two indices of the
vertices the edge passes between.
-------------------------------------------------- %
%-------------------------------------------------
Shortcuts a set or trees so that all vertices point to the root
-------------------------------------------------- %
function shortcut_all(p) =
let pp = p->p;
in if eql(p,pp) then pp else shortcut_all(pp) $
%-------------------------------------------------
Updates edge with values on the vertices they point to
-------------------------------------------------- %
function update_edges(E,V) =
{i,v1,v2 : i,e1,e2 in E
; v1 in v->{e1:(i,e1,e2) in E}
; v2 in v->{e2:(i,e1,e2) in E}} $
%-------------------------------------------------
This routine hooks the vertices using the edges to form a
set of trees, shortcuts until all vertices point to a root,
and then updates the edges to point to the root. The
hooking is either from vertices with a larger index to ones
with a smaller index, or the other way around depending on
the whether direction is true or false. The routine returns
the parent pointers, the updated edges and the identifiers
of edges that participated in the hook.
-------------------------------------------------- %
function hook_and_shortcut(P,E,direction) =
let
% Flip edges so they all point in the same direction.
This guarantees that we do not generate cycles when hooking %
e = {i,if (e1 > e2 xor direction) then (e1,e2) else (e2,e1)
: i,(e1,e2) in e};
% Attemp a hook by writing edge identifier to vertex %
a = [0:#P]<-{e1,i : i,e1,e2 in E};
% Check which edges successfully hooked %
TI,TE = unzip({i,e in E
; j in a->{e1: i,e1,e2 in E}
| i == j});
% Now execute actual hook, and then shortcut %
P = shortcut_all(P<-TE);
% update edges to point to new parent %
E = update_edges(E,P);
% remove self edges %
E = {i,(e1,e2) in E | e1 /= e2};
in P,E,TI $
%-----------------------------------------------
Finds the spanning of a graph represented as an edgelist.
The input E is a sequence of edges each of which consits of an
indentifier along with the indices of the two endpoints.
n is the number of vertices in the graph.
The algorithm returns the identifiers of the edges on a
spanning tree.
----------------------------------------------- %
function spanning_tree(E,n) =
if #E == 0 then [] int
else
let
% Hook from larger to smaller %
(P,E,TI_D) = hook_and_shortcut([0:n],E,t);
% Hook from smaller to larger %
(P,E,TI_U) = hook_and_shortcut(P,E,f);
% Compress graph by identifying the roots and
relabeling the edges %
roots = {p == i : p in P ; i in [0:n]};
labels = enumerate(roots);
E = update_edges(e,labels);
n = count(roots);
% Recurse -- the new n is at most 1/2 the original n so
that the total number of recursive calls is O(lg n) %
in TI_D ++ TI_U ++ spanning_tree(E,n) $