Menu
Coddy logo textTech

Algoritmo di Kruskal

Ultimo aggiornamento

L'algoritmo di Kruskal costruisce un albero ricoprente minimo (MST), cioè l'insieme di archi più economico che collega tutti i nodi senza cicli. Ordina tutti gli archi per peso, poi aggiunge in modo greedy l'arco successivo più economico, a patto che unisca due nodi non ancora collegati. Premi play qui sopra per vedere l'albero crescere, un arco sicuro più economico alla volta.

Il controllo "già collegati?" si fa con una struttura dati union-find (insiemi disgiunti): ogni arco viene saltato se i suoi due estremi sono già nella stessa componente, perché aggiungerlo formerebbe un ciclo. L'ordinamento domina il costo, per un totale di O(E log E). Kruskal dà il meglio sui grafi sparsi.

Complessità temporale e spaziale

MisuraComplessitàNote
TempoO(E log E)Dominato dall'ordinamento degli archi
Operazioni union-find≈ O(E α(V))Quasi costanti per controllo con la compressione dei cammini
SpazioO(V + E)Lista degli archi + struttura a insiemi disgiunti
Ideale perGrafi sparsiLavora su una lista globale degli archi

Passo dopo passo

PassoCosa succede
1Ordina tutti gli archi per peso, in ordine crescente.
2Metti ogni nodo in una componente a sé (union-find).
3Prendi l'arco successivo più economico.
4Se i suoi estremi sono in componenti diverse, aggiungilo all'albero e uniscile.
5Altrimenti saltalo (creerebbe un ciclo).
6Fermati quando l'albero ha V − 1 archi.

Esempio svolto

Grafo con i nodi A, B, C, D e gli archi A-B(1), B-C(2), A-C(3), C-D(4), B-D(5). Archi ordinati: A-B(1), B-C(2), A-C(3), C-D(4), B-D(5):

Arco (peso)Componenti primaAzione
A-B(1){A} {B} {C} {D}Componenti diverse: aggiungi all'MST, unisci in {A,B}.
B-C(2){A,B} {C} {D}Componenti diverse: aggiungi all'MST, unisci in {A,B,C}.
A-C(3){A,B,C} {D}A e C sono già insieme: salta (formerebbe un ciclo).
C-D(4){A,B,C} {D}Componenti diverse: aggiungi all'MST, unisci in {A,B,C,D}.
Stop{A,B,C,D}L'albero ha V - 1 = 3 archi. MST = A-B, B-C, C-D, peso totale 7.

Quando usare l'algoritmo di Kruskal

Usalo quandoEvitalo quando
Il grafo è sparso (pochi archi rispetto ai nodi).Il grafo è denso: Prim con un heap di solito è più veloce.
Gli archi sono già disponibili come lista globale da ordinare.Gli archi arrivano solo tramite liste di adiacenza da scorrere nodo per nodo.
Vuoi un'implementazione semplice basata su union-find.Ti serve che l'albero cresca da uno specifico nodo di partenza.
Vuoi costruire una foresta ricoprente di un grafo non connesso.Devi gestire archi che arrivano in streaming senza un ordinamento completo.

Codice Kruskal's Algorithm

Un'implementazione di Kruskal's Algorithm pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.

Codice Kruskal's Algorithm in Python

Python
1def find(parent, x):2    while parent[x] != x:3        parent[x] = parent[parent[x]]  # path compression4        x = parent[x]5    return x6
7
8def kruskal(vertices, edges):9    # Take the cheapest edge that does not close a cycle10    parent = {v: v for v in vertices}11    mst, total = [], 012    for w, u, v in sorted(edges):13        root_u, root_v = find(parent, u), find(parent, v)14        if root_u != root_v:15            parent[root_u] = root_v16            mst.append((u, v, w))17            total += w18    return mst, total19
20
21vertices = ["A", "B", "C", "D", "E"]22edges = [23    (4, "A", "B"), (1, "A", "C"), (3, "B", "C"), (2, "B", "D"),24    (5, "C", "D"), (6, "C", "E"), (7, "D", "E"),25]26
27mst, total = kruskal(vertices, edges)28for u, v, w in mst:29    print(f"{u} - {v} (weight {w})")30print("Total MST weight:", total)
Esegui questo codice nel playground Python

Domande frequenti sull'algoritmo di Kruskal

Cos'è un albero ricoprente minimo?
Un albero ricoprente minimo (MST) di un grafo pesato connesso è un sottoinsieme di archi che collega tutti i nodi senza cicli e con il peso totale più piccolo possibile. Ha esattamente V - 1 archi per V nodi.
Qual è la differenza tra l'algoritmo di Kruskal e quello di Prim?
Entrambi costruiscono un albero ricoprente minimo in modo greedy. Kruskal ordina globalmente tutti gli archi e aggiunge il più economico che non forma un ciclo, usando l'union-find. Prim fa crescere un unico albero verso l'esterno a partire da un nodo iniziale, aggiungendo sempre l'arco più economico che esce dall'albero. Kruskal è adatto ai grafi sparsi; Prim (con un heap) a quelli densi.
Perché l'algoritmo di Kruskal usa l'union-find?
Prima di aggiungere un arco, Kruskal deve controllare se i suoi due estremi sono già collegati: aggiungere un arco del genere creerebbe un ciclo. L'union-find (insiemi disgiunti) risponde a "sono nella stessa componente?" e unisce le componenti in un tempo ammortizzato quasi costante, mantenendo l'algoritmo efficiente.
Quando conviene usare l'algoritmo di Kruskal invece di quello di Prim?
Preferisci Kruskal quando il grafo è sparso e hai già tutti gli archi in una lista da ordinare: l'ordinamento O(E log E) costa poco quando E è piccolo. L'algoritmo di Prim con un heap binario o di Fibonacci tende a vincere sui grafi densi dove E si avvicina a V², perché evita di ordinare tutti gli archi in anticipo.
L'algoritmo di Kruskal funziona sui grafi non connessi?
Sì. Se il grafo non è connesso, Kruskal semplicemente non riesce ad arrivare a V - 1 archi e produce invece una foresta ricoprente minima, un MST per ogni componente connessa. Succede in modo naturale, perché l'union-find non unisce mai nodi tra cui non esiste un percorso.
Qual è l'errore più comune quando si implementa l'algoritmo di Kruskal?
Tralasciare la compressione dei cammini e l'unione per rango dell'union-find, cosa che porta ogni controllo di connessione da quasi costante a quasi lineare e può dominare il tempo di esecuzione. Un altro bug frequente è dimenticare di unire davvero le due componenti dopo aver aggiunto un arco, lasciando che gli archi successivi creino cicli.
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA