Kruskalův algoritmus slouží k nalezení minimální kostry grafu (kostry takové, že součet vah jejích hran je minimální). Algoritmus byl poprvé publikován v roce 1956 Josephem Kruskalem.
Princip
Kruskalův algoritmus nejprve setřídí hrany dle jejich váhy (od nejmenší) a následně přidává hrany do grafu takovým způsobem, aby nevznikl žádný cyklus (tj. procedura terminuje po přidání hran).
K zajištění acykličnosti si algoritmus pomocí datové struktury disjoint set udržuje pro každý uzel informaci o příslušnosti ke komponentě souvislosti. Disjoint set poskytuje dvě operace: union (spojí dvě komponenty souvislosti) a find (zjistí pro daný uzel příslušnost ke komponentě souvislosti).
Asymptotická složitost algoritmu
Časová složitost algoritmu je v případě použití řadicího algoritmu založeného na porovnávání . Pokud jsou hrany již předřazeny, nebo je možno k jejich seřazení použít řadicí algoritmus s lineární složitostí (např.
counting sort), tak je složitost Kruskalova algoritmu rovna
, kde
je inverzní Ackermannova funkce (odpovídá složitosti operací union a find).
Kód
01.
/**
02.
* Kruskaluv algoritmus
03.
* @param graph graf
04.
* @param w vahy jednotlivych hran
05.
* @return minimalni kostra grafu
06.
*/
07.
SpanningTree kruskalAlgorithm(Graph graph, weights w)
08.
SpanningTree tree
//kostra
09.
for
Node n : graph
do
10.
makeSet(n)
//kazdy uzel je v samostatnem setu
11.
List edges = sort(graph.getEdges(), w)
//seradi hrany podle vah
12.
13.
for
Edge e in edges
do
14.
if
findSet(e.start) != findSet(e.end)
//pokud jsou pocatecni a koncovy uzel v ruznych setech (netvorime cyklus)
15.
tree.add(e)
//pridej hranu do kostry
16.
union(e.start, e.end)
//sety spojime v jeden
17.
if
tree.edgeCount() == graph.nodeCount() -
1
//pokud uz je kostra cela
18.
break
//konec pridavani hran
19.
return
tree
001.
/**
002.
* Kruskaluv algoritmus
003.
* @author Pavel Micka
004.
*/
005.
public
class
KruskalAlgorithm {
006.
/**
007.
* Nalezne minimalni kostru neorientovaneho grafu o n uzlech (cisla uzlu
008.
* 0, 1, ..., n-1)
009.
* @param edges hrany neorientovaneho grafu
010.
* @param nodeCount pocet uzlu neorientovaneho grafu (n)
011.
* @return minimalni kostra
012.
*/
013.
public
static
List<Edge> kruskalAlgorithm(List<Edge> edges,
int
nodeCount) {
014.
DisjointSet ds =
new
DisjointSet(nodeCount);
015.
List<Edge> spanningTree =
new
ArrayList<Edge>();
016.
Collections.sort(edges);
017.
int
i =
0
;
018.
while
(i != edges.size() && spanningTree.size() != nodeCount -
1
) {
019.
Edge e = edges.get(i);
020.
if
(ds.find(e.getFrom()) != ds.find(e.getTo())){
021.
spanningTree.add(e);
022.
ds.union(e.getFrom(), e.getTo());
023.
}
024.
i++;
025.
}
026.
return
spanningTree;
027.
}
028.
}
029.
030.
031.
// ***************************** //
032.
// * DATOVE STRUKTURY * //
033.
// ***************************** //
034.
035.
036.
/**
037.
* Hrana neorientovaneho grafu
038.
* @author Pavel Micka
039.
*/
040.
class
Edge
implements
Comparable<Edge> {
041.
private
int
from;
//uzel z
042.
private
int
to;
//uzel do
043.
private
int
weight;
//vaha
044.
public
Edge(
int
from,
int
to,
int
weight) {
045.
this
.from = from;
046.
this
.to = to;
047.
this
.weight = weight;
048.
}
049.
/**
050.
* Provede porovnani hran dle vahy (vyssi vaha == vetsi)
051.
* @param o
052.
* @return
053.
*/
054.
public
int
compareTo(Edge o) {
055.
if
(
this
.getWeight() > o.getWeight()) {
056.
return
1
;
057.
}
else
if
(
this
.getWeight() < o.getWeight()) {
058.
return
-
1
;
059.
}
else
{
060.
return
0
;
061.
}
062.
}
063.
064.
/**
065.
* @return the from
066.
*/
067.
public
int
getFrom() {
068.
return
from;
069.
}
070.
071.
/**
072.
* @param from the from to set
073.
*/
074.
public
void
setFrom(
int
from) {
075.
this
.from = from;
076.
}
077.
078.
/**
079.
* @return the to
080.
*/
081.
public
int
getTo() {
082.
return
to;
083.
}
084.
085.
/**
086.
* @param to the to to set
087.
*/
088.
public
void
setTo(
int
to) {
089.
this
.to = to;
090.
}
091.
092.
/**
093.
* @return the weight
094.
*/
095.
public
int
getWeight() {
096.
return
weight;
097.
}
098.
099.
/**
100.
* @param weight the weight to set
101.
*/
102.
public
void
setWeight(
int
weight) {
103.
this
.weight = weight;
104.
}
105.
}
106.
/**
107.
* Jednoducha implementace Union-Find problemu
108.
* @author Pavel Micka
109.
*/
110.
class
DisjointSet {
111.
112.
private
Node[] nodes;
113.
114.
public
DisjointSet(
int
nodeCount) {
115.
nodes =
new
Node[nodeCount];
116.
for
(
int
i =
0
; i < nodeCount; i++) {
117.
nodes[i] =
new
Node();
118.
nodes[i].id = i;
119.
}
120.
}
121.
122.
/**
123.
* Provede sjednoceni komponent, ve kterych jsou uzly "a" a "b"
124.
* Union provadi vzdy do A
125.
* @param a cislo uzlu a
126.
* @param b cislo uzlu b
127.
* @return cislo reprezentanta sjednocene komponenty
128.
*/
129.
public
int
union(
int
a,
int
b) {
130.
Node repA = nodes[find(a)];
131.
Node repB = nodes[find(b)];
132.
133.
repB.parent = repA;
134.
return
repA.id;
135.
}
136.
137.
/**
138.
* Vrati reprezentanta zadaneho uzlu
139.
* @param a cislo uzlu, jehoz reprezentanta hledame
140.
* @return cislo reprezentanta uzlu
141.
*/
142.
public
int
find(
int
a) {
143.
Node n = nodes[a];
144.
int
jumps =
0
;
145.
while
(n.parent !=
null
) {
146.
n = n.parent;
147.
jumps++;
148.
}
149.
if
(jumps >
1
) repair(a, n.id);
150.
return
n.id;
151.
}
152.
153.
/**
154.
* Provede opravu (compression) cesty
155.
* @param a
156.
* @param rootId
157.
*/
158.
private
void
repair(
int
a,
int
rootId) {
159.
Node curr = nodes[a];
160.
while
(curr.id != rootId) {
161.
Node tmp = curr.parent;
162.
curr.parent = nodes[rootId];
163.
curr = tmp;
164.
}
165.
}
166.
167.
@Override
168.
public
String toString() {
169.
StringBuilder builder =
new
StringBuilder();
170.
for
(
int
i =
0
; i < nodes.length; i++) {
171.
builder.append(find(i) +
" "
);
172.
}
173.
return
builder.toString();
174.
}
175.
176.
/**
177.
* Uzel n-arniho stromu
178.
*/
179.
private
static
class
Node {
180.
/**
181.
* Rodic
182.
*/
183.
Node parent;
184.
/**
185.
* Identifikator uzlu
186.
*/
187.
int
id;
188.
}
189.
}
Literatura
- KOLÁŘ, Josef. Teoretická informatika. 2. vyd. Praha : Česká informatická společnost, 2004. 205 s. ISBN 80-900853-8-5.