Kruskalův algoritmus

Kruskalův algoritmus
Kruskalův algoritmus

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í \vert U \vert -1 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í O(\vert H\vert \cdot \log \vert H \vert). 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 O(\vert H\vert \cdot \alpha(\vert H \vert)), kde \alpha je inverzní Ackermannova funkce (odpovídá složitosti operací union a find).

Kód

/**
 * Kruskaluv algoritmus
 * @param graph graf
 * @param w vahy jednotlivych hran
 * @return minimalni kostra grafu
 */
SpanningTree kruskalAlgorithm(Graph graph, weights w)
    SpanningTree tree //kostra
    for Node n : graph do
        makeSet(n) //kazdy uzel je v samostatnem setu
    List edges = sort(graph.getEdges(), w) //seradi hrany podle vah
    
    for Edge e in edges do
        if findSet(e.start) != findSet(e.end) //pokud jsou pocatecni a koncovy uzel v ruznych setech (netvorime cyklus)
            tree.add(e) //pridej hranu do kostry
            union(e.start, e.end) //sety spojime v jeden
            if tree.edgeCount() == graph.nodeCount() - 1 //pokud uz je kostra cela
                break //konec pridavani hran
    return tree
/**
 * Kruskaluv algoritmus
 * @author Pavel Micka
 */
public class KruskalAlgorithm {
    /**
     * Nalezne minimalni kostru neorientovaneho grafu o n uzlech (cisla uzlu
     * 0, 1, ..., n-1)
     * @param edges hrany neorientovaneho grafu
     * @param nodeCount pocet uzlu neorientovaneho grafu (n)
     * @return minimalni kostra
     */
    public static List<Edge> kruskalAlgorithm(List<Edge> edges, int nodeCount) {
        DisjointSet ds = new DisjointSet(nodeCount);
        List<Edge> spanningTree = new ArrayList<Edge>();
        Collections.sort(edges);
        int i = 0;
        while (i != edges.size() && spanningTree.size() != nodeCount - 1) {
            Edge e = edges.get(i);
            if(ds.find(e.getFrom()) != ds.find(e.getTo())){
                spanningTree.add(e);
                ds.union(e.getFrom(), e.getTo());
            }
            i++;
        }
        return spanningTree;
    }
}


// ***************************** //
// *     DATOVE STRUKTURY      * //
// ***************************** //


/**
 * Hrana neorientovaneho grafu
 * @author Pavel Micka
 */
class Edge implements Comparable<Edge> {
    private int from; //uzel z
    private int to; //uzel do
    private int weight; //vaha
    public Edge(int from, int to, int weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
    }
    /**
     * Provede porovnani hran dle vahy (vyssi vaha == vetsi)
     * @param o
     * @return
     */
    public int compareTo(Edge o) {
        if (this.getWeight() > o.getWeight()) {
            return 1;
        } else if (this.getWeight() < o.getWeight()) {
            return -1;
        } else {
            return 0;
        }
    }

    /**
     * @return the from
     */
    public int getFrom() {
        return from;
    }

    /**
     * @param from the from to set
     */
    public void setFrom(int from) {
        this.from = from;
    }

    /**
     * @return the to
     */
    public int getTo() {
        return to;
    }

    /**
     * @param to the to to set
     */
    public void setTo(int to) {
        this.to = to;
    }

    /**
     * @return the weight
     */
    public int getWeight() {
        return weight;
    }

    /**
     * @param weight the weight to set
     */
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
/**
 * Jednoducha implementace Union-Find problemu
 * @author Pavel Micka
 */
class DisjointSet {

    private Node[] nodes;

    public DisjointSet(int nodeCount) {
        nodes = new Node[nodeCount];
        for (int i = 0; i < nodeCount; i++) {
            nodes[i] = new Node();
            nodes[i].id = i;
        }
    }

    /**
     * Provede sjednoceni komponent, ve kterych jsou uzly "a" a "b"
     * Union provadi vzdy do A
     * @param a cislo uzlu a
     * @param b cislo uzlu b
     * @return cislo reprezentanta sjednocene komponenty
     */
    public int union(int a, int b) {
        Node repA = nodes[find(a)];
        Node repB = nodes[find(b)];

        repB.parent = repA;
        return repA.id;
    }

    /**
     * Vrati reprezentanta zadaneho uzlu
     * @param a cislo uzlu, jehoz reprezentanta hledame
     * @return cislo reprezentanta uzlu
     */
    public int find(int a) {
        Node n = nodes[a];
        int jumps = 0;
        while (n.parent != null) {
            n = n.parent;
            jumps++;
        }
        if (jumps > 1) repair(a, n.id);
        return n.id;
    }

    /**
     * Provede opravu (compression) cesty
     * @param a
     * @param rootId
     */
    private void repair(int a, int rootId) {
        Node curr = nodes[a];
        while (curr.id != rootId) {
            Node tmp = curr.parent;
            curr.parent = nodes[rootId];
            curr = tmp;
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < nodes.length; i++) {
            builder.append(find(i) + " ");
        }
        return builder.toString();
    }

    /**
     * Uzel n-arniho stromu
     */
    private static class Node {
        /**
         * Rodic
         */
        Node parent;
        /**
         * Identifikator uzlu
         */
        int id;
    }
}

Literatura

  • KOLÁŘ, Josef. Teoretická informatika. 2. vyd. Praha : Česká informatická společnost, 2004. 205 s. ISBN 80-900853-8-5.
Hodnocení (0): 0

Přečtěte si také

Diskuse





Pavel Mička1.2.2011
Dojde tím v Disjoint-setu ke spojení obou komponent.
Petr1.2.2011
Mohu se zeptat, proč se řádku 22 volá metoda union, která vrací int, když se vrácená hodnota nikam nepřiřazuje?