logo

Algorytm minimalnego drzewa rozpinającego Kruskala (MST).

Minimalne drzewo rozpinające (MST) lub drzewo rozpinające o minimalnej wadze dla ważonego, spójnego i nieskierowanego wykresu to drzewo rozpinające o wadze mniejszej lub równej wadze każdego innego drzewa rozpinającego. Aby dowiedzieć się więcej na temat minimalnego drzewa rozpinającego, zobacz Ten artykuł .

Wprowadzenie do algorytmu Kruskala:

Tutaj omówimy Algorytm Kruskala aby znaleźć MST danego wykresu ważonego.

W algorytmie Kruskala posortuj wszystkie krawędzie danego grafu w kolejności rosnącej. Następnie dodaje nowe krawędzie i węzły w MST, jeśli nowo dodana krawędź nie tworzy cyklu. Najpierw wybiera minimalnie ważoną krawędź, a na koniec maksymalnie ważoną krawędź. Można zatem powiedzieć, że na każdym etapie dokonuje lokalnie optymalnego wyboru w celu znalezienia optymalnego rozwiązania. Dlatego jest to Poniżej znajdują się kroki, aby znaleźć MST za pomocą algorytmu Kruskala:



  1. Posortuj wszystkie krawędzie w kolejności niemalejącej ich wagi.
  2. Wybierz najmniejszą krawędź. Sprawdź, czy tworzy cykl z utworzonym dotychczas drzewem rozpinającym. Jeśli cykl nie jest utworzony, uwzględnij tę krawędź. W przeciwnym razie wyrzuć to.
  3. Powtarzaj krok nr 2, aż w drzewie rozpinającym pojawią się krawędzie (V-1).

Krok 2 używa Algorytm Union-Find do wykrywania cykli.

Dlatego zalecamy przeczytanie poniższego postu jako warunku wstępnego.

  • Algorytm znajdowania Unii | Zestaw 1 (wykryj cykl na wykresie)
  • Algorytm znajdowania Unii | Zestaw 2 (suma według rangi i kompresji ścieżki)

Algorytm Kruskala służący do znalezienia drzewa rozpinającego koszt minimalny wykorzystuje podejście zachłanne. Chciwy wybór polega na wybraniu najmniejszej krawędzi ciężaru, która nie powoduje cyklu w dotychczas skonstruowanym MST. Wyjaśnijmy to na przykładzie:

posortowana lista tablic Java

Ilustracja:

Poniżej znajduje się ilustracja powyższego podejścia:

Wykres wejściowy:

Algorytm minimalnego drzewa rozpinającego Kruskala

Wykres zawiera 9 wierzchołków i 14 krawędzi. Zatem minimalne utworzone drzewo rozpinające będzie miało (9 – 1) = 8 krawędzi.
Po sortowaniu:

Waga Źródło Miejsce docelowe
1 7 6
2 8 2
2 6 5
4 0 1
4 2 5
6 8 6
7 2 3
7 7 8
8 0 7
8 1 2
9 3 4
10 5 4
jedenaście 1 7
14 3 5

Teraz wybierz wszystkie krawędzie jedna po drugiej z posortowanej listy krawędzi

Krok 1: Wybierz krawędź 7-6. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 7-6 w MST

Dodaj krawędź 7-6 w MST

Krok 2: Wybierz przewagę 8-2. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 8-2 w MST

Dodaj krawędź 8-2 w MST

Krok 3: Wybierz krawędź 6-5. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 6-5 w MST

Dodaj krawędź 6-5 w MST

Krok 4: Wybierz przewagę 0-1. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 0-1 w MST

Dodaj krawędź 0-1 w MST

Krok 5: Wybierz krawędź 2-5. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 0-1 w MST

Dodaj krawędź 2-5 w MST

Krok 6: Wybierz krawędź 8-6. Ponieważ włączenie tej krawędzi skutkuje cyklem, należy ją odrzucić. Wybierz krawędź 2-3: Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 2-3 w MST

Dodaj krawędź 2-3 w MST

Krok 7: Wybierz krawędź 7-8. Ponieważ włączenie tej krawędzi skutkuje cyklem, należy ją odrzucić. Wybierz krawędź 0-7. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 0-7 w MST

Dodaj krawędź 0-7 w MST

Krok 8: Wybierz krawędź 1-2. Ponieważ włączenie tej krawędzi skutkuje cyklem, należy ją odrzucić. Wybierz krawędź 3-4. Nie tworzy się żaden cykl, uwzględnij go.

Dodaj krawędź 3-4 w MST

Dodaj krawędź 3-4 w MST

Notatka: Ponieważ liczba krawędzi zawartych w MST jest równa (V – 1), więc algorytm zatrzymuje się w tym miejscu

Poniżej implementacja powyższego podejścia:

C++




// C++ program for the above approach> > #include> using> namespace> std;> > // DSU data structure> // path compression + rank by union> class> DSU {> >int>* parent;> >int>* rank;> > public>:> >DSU(>int> n)> >{> >parent =>new> int>[n];> >rank =>new> int>[n];> > >for> (>int> i = 0; i parent[i] = -1; rank[i] = 1; } } // Find function int find(int i) { if (parent[i] == -1) return i; return parent[i] = find(parent[i]); } // Union function void unite(int x, int y) { int s1 = find(x); int s2 = find(y); if (s1 != s2) { if (rank[s1] parent[s1] = s2; } else if (rank[s1]>ranga[s2]) { rodzic[s2] = s1; } else { rodzic[s2] = s1; ranga[s1] += 1; } } } }; klasa Wykres { wektorint>> lista krawędzi; w telewizji; public: Graph(int V) { this->V = V; } // Funkcja dodająca krawędź na wykresie void addEdge(int x, int y, int w) { Edgelist.push_back({ w, x, y }); } void kruskals_mst() { // Sortuj wszystkie krawędzie sort(edgelist.begin(), Edgelist.end()); // Zainicjuj DSU DSU s(V); int odp = 0; cout<< 'Following are the edges in the ' 'constructed MST' << endl; for (auto edge : edgelist) { int w = edge[0]; int x = edge[1]; int y = edge[2]; // Take this edge in MST if it does // not forms a cycle if (s.find(x) != s.find(y)) { s.unite(x, y); ans += w; cout << x << ' -- ' << y << ' == ' << w << endl; } } cout << 'Minimum Cost Spanning Tree: ' << ans; } }; // Driver code int main() { Graph g(4); g.addEdge(0, 1, 10); g.addEdge(1, 3, 15); g.addEdge(2, 3, 4); g.addEdge(2, 0, 6); g.addEdge(0, 3, 5); // Function call g.kruskals_mst(); return 0; }>

>

>

C




// C code to implement Kruskal's algorithm> > #include> #include> > // Comparator function to use in sorting> int> comparator(>const> void>* p1,>const> void>* p2)> {> >const> int>(*x)[3] = p1;> >const> int>(*y)[3] = p2;> > >return> (*x)[2] - (*y)[2];> }> > // Initialization of parent[] and rank[] arrays> void> makeSet(>int> parent[],>int> rank[],>int> n)> {> >for> (>int> i = 0; i parent[i] = i; rank[i] = 0; } } // Function to find the parent of a node int findParent(int parent[], int component) { if (parent[component] == component) return component; return parent[component] = findParent(parent, parent[component]); } // Function to unite two sets void unionSet(int u, int v, int parent[], int rank[], int n) { // Finding the parents u = findParent(parent, u); v = findParent(parent, v); if (rank[u] parent[u] = v; } else if (rank[u]>ranga[v]) { rodzic[v] = u; } else { rodzic[v] = ty; // Ponieważ ranga wzrasta, // jeśli rangi dwóch zestawów są takie same rang[u]++; } } // Funkcja znajdująca MST void kruskalAlgo(int n, int Edge[n][3]) { // Najpierw sortujemy tablicę krawędzi w porządku rosnącym // abyśmy mogli uzyskać dostęp do minimalnych odległości/kosztu qsort(edge , n, rozmiar(krawędź[0]), komparator); int rodzic[n]; int ranga[n]; // Funkcja inicjująca rodzica[] i rangę[] makeSet(parent, rank, n); // Aby zapisać koszt minimalny int minCost = 0; printf( 'Następujące krawędzie skonstruowanego MST '); for (int i = 0; i int v1 = findParent(nadrzędny, krawędź [i] [0]); int v2 = findParent (nadrzędny, krawędź [i] [1]); int wt = krawędź [i] [2] ; // Jeśli rodzice są różni, // oznacza to, że znajdują się w różnych zbiorach, więc // połącz ich if (v1 != v2) { unionSet(v1, v2, parent, rank, n); '%d -- %d == %d ', krawędź[i][0], krawędź[i][1], wt); printf('Drzewo rozpinające minimalny koszt: %d n', minCost); } // Kod sterownika int main() { int Edge[5][3] = { { 0, 1, 10 }, { 0, 2, 6 }, { 0, 3, 5 } , { 1, 3, 15 }, { 2, 3, 4 } }; kruskalAlgo(5, krawędź); return 0 }>

>

przechodzenie przez pocztę

>

Jawa




// Java program for Kruskal's algorithm> > import> java.util.ArrayList;> import> java.util.Comparator;> import> java.util.List;> > public> class> KruskalsMST {> > >// Defines edge structure> >static> class> Edge {> >int> src, dest, weight;> > >public> Edge(>int> src,>int> dest,>int> weight)> >{> >this>.src = src;> >this>.dest = dest;> >this>.weight = weight;> >}> >}> > >// Defines subset element structure> >static> class> Subset {> >int> parent, rank;> > >public> Subset(>int> parent,>int> rank)> >{> >this>.parent = parent;> >this>.rank = rank;> >}> >}> > >// Starting point of program execution> >public> static> void> main(String[] args)> >{> >int> V =>4>;> >List graphEdges =>new> ArrayList(> >List.of(>new> Edge(>0>,>1>,>10>),>new> Edge(>0>,>2>,>6>),> >new> Edge(>0>,>3>,>5>),>new> Edge(>1>,>3>,>15>),> >new> Edge(>2>,>3>,>4>)));> > >// Sort the edges in non-decreasing order> >// (increasing with repetition allowed)> >graphEdges.sort(>new> Comparator() {> >@Override> public> int> compare(Edge o1, Edge o2)> >{> >return> o1.weight - o2.weight;> >}> >});> > >kruskals(V, graphEdges);> >}> > >// Function to find the MST> >private> static> void> kruskals(>int> V, List edges)> >{> >int> j =>0>;> >int> noOfEdges =>0>;> > >// Allocate memory for creating V subsets> >Subset subsets[] =>new> Subset[V];> > >// Allocate memory for results> >Edge results[] =>new> Edge[V];> > >// Create V subsets with single elements> >for> (>int> i =>0>; i subsets[i] = new Subset(i, 0); } // Number of edges to be taken is equal to V-1 while (noOfEdges 1) { // Pick the smallest edge. And increment // the index for next iteration Edge nextEdge = edges.get(j); int x = findRoot(subsets, nextEdge.src); int y = findRoot(subsets, nextEdge.dest); // If including this edge doesn't cause cycle, // include it in result and increment the index // of result for next edge if (x != y) { results[noOfEdges] = nextEdge; union(subsets, x, y); noOfEdges++; } j++; } // Print the contents of result[] to display the // built MST System.out.println( 'Following are the edges of the constructed MST:'); int minCost = 0; for (int i = 0; i System.out.println(results[i].src + ' -- ' + results[i].dest + ' == ' + results[i].weight); minCost += results[i].weight; } System.out.println('Total cost of MST: ' + minCost); } // Function to unite two disjoint sets private static void union(Subset[] subsets, int x, int y) { int rootX = findRoot(subsets, x); int rootY = findRoot(subsets, y); if (subsets[rootY].rank subsets[rootY].parent = rootX; } else if (subsets[rootX].rank subsets[rootX].parent = rootY; } else { subsets[rootY].parent = rootX; subsets[rootX].rank++; } } // Function to find parent of a set private static int findRoot(Subset[] subsets, int i) { if (subsets[i].parent == i) return subsets[i].parent; subsets[i].parent = findRoot(subsets, subsets[i].parent); return subsets[i].parent; } } // This code is contributed by Salvino D'sa>

>

>

Python3




# Python program for Kruskal's algorithm to find> # Minimum Spanning Tree of a given connected,> # undirected and weighted graph> > > # Class to represent a graph> class> Graph:> > >def> __init__(>self>, vertices):> >self>.V>=> vertices> >self>.graph>=> []> > ># Function to add an edge to graph> >def> addEdge(>self>, u, v, w):> >self>.graph.append([u, v, w])> > ># A utility function to find set of an element i> ># (truly uses path compression technique)> >def> find(>self>, parent, i):> >if> parent[i] !>=> i:> > ># Reassignment of node's parent> ># to root node as> ># path compression requires> >parent[i]>=> self>.find(parent, parent[i])> >return> parent[i]> > ># A function that does union of two sets of x and y> ># (uses union by rank)> >def> union(>self>, parent, rank, x, y):> > ># Attach smaller rank tree under root of> ># high rank tree (Union by Rank)> >if> rank[x] parent[x] = y elif rank[x]>rank[y]: parent[y] = x # Jeśli rangi są takie same, utwórz jeden jako pierwiastek # i zwiększ jego rangę o jeden inny: parent[y] = x rank[x] += 1 # Główna funkcja do skonstruowania MST # przy użyciu algorytmu Kruskala def KruskalMST(self): # Zapisuje wynikowy wynik MST = [] # Zmienna indeksująca, używana do sortowania krawędzi i = 0 # Zmienna indeksująca, używana do wyniku[] e = 0 # Sortuj wszystkie krawędzie w # niemalejącej kolejności ich # wagi self.graph = sorted(self.graph, key=lambda item: item[2]) parent = [] rank = [] # Utwórz V podzbiory z pojedynczymi elementami dla węzła w zakresie(self.V): parent.append(node) rank.append(0) # Liczba krawędzi do wykorzystania jest mniejsza niż V-1 podczas e

>

>

C#

ciąg znaków w tablicy c




// C# Code for the above approach> > using> System;> > class> Graph {> > >// A class to represent a graph edge> >class> Edge : IComparable {> >public> int> src, dest, weight;> > >// Comparator function used for sorting edges> >// based on their weight> >public> int> CompareTo(Edge compareEdge)> >{> >return> this>.weight - compareEdge.weight;> >}> >}> > >// A class to represent> >// a subset for union-find> >public> class> subset {> >public> int> parent, rank;> >};> > >// V->NIE. wierzchołków & E->liczba krawędzi> >int> V, E;> > >// Collection of all edges> >Edge[] edge;> > >// Creates a graph with V vertices and E edges> >Graph(>int> v,>int> e)> >{> >V = v;> >E = e;> >edge =>new> Edge[E];> >for> (>int> i = 0; i edge[i] = new Edge(); } // A utility function to find set of an element i // (uses path compression technique) int find(subset[] subsets, int i) { // Find root and make root as // parent of i (path compression) if (subsets[i].parent != i) subsets[i].parent = find(subsets, subsets[i].parent); return subsets[i].parent; } // A function that does union of // two sets of x and y (uses union by rank) void Union(subset[] subsets, int x, int y) { int xroot = find(subsets, x); int yroot = find(subsets, y); // Attach smaller rank tree under root of // high rank tree (Union by Rank) if (subsets[xroot].rank subsets[xroot].parent = yroot; else if (subsets[xroot].rank>podzbiory[yroot].rank) podzbiory[yroot].parent = xroot; // Jeśli rangi są takie same, utwórz jedną jako root // i zwiększ jej rangę o jeszcze jedną { subsets[yroot].parent = xroot; podzbiory[xroot].rank++; } } // Główna funkcja konstruująca MST // przy użyciu algorytmu Kruskala void KruskalMST() { // Będzie przechowywać // wynikowy MST Edge[] wynik = new Edge[V]; // Zmienna indeksowa używana dla wyniku[] int e = 0; // Zmienna indeksująca używana do sortowania krawędzi int i = 0; for (i = 0; iresult[i] = new Edge(); // Posortuj wszystkie krawędzie w niemalejącej // kolejności ich wag. Jeśli nie wolno nam // zmieniać danego wykresu, możemy go utworzyć // kopia tablicy krawędzi Array.Sort(edge); // Przydziel pamięć do tworzenia podzbiorów V subset[] subsets = new subset[V] for (i = 0; i subsets[i] = new subset() ; // Utwórz V podzbiory z pojedynczymi elementami for (int v = 0; v subsets[v].parent = v; subsets[v].rank = 0; } i = 0; // Liczba krawędzi do wzięcia jest równa do V-1 while (e // Wybierz najmniejszą krawędź. I zwiększ // indeks dla następnej iteracji Edge next_edge = new Edge(); next_edge = Edge[i++]; int x = find(subsets, next_edge.src); int y = find(subsets, next_edge.dest); // Jeśli włączenie tej krawędzi nie powoduje cyklu, // uwzględnij ją w wyniku i zwiększ indeks // wyniku dla następnej krawędzi if (x != y) { wynik[e++] = next_edge; Union(subsets, x, y); skonstruowany MST'); int minimalny koszt = 0; for (i = 0; i Console.WriteLine(result[i].src + ' -- ' + wynik[i].dest + ' == ' + wynik[i].waga); minimumCost += wynik[i].waga; } Console.WriteLine('Drzewo rozpinające minimalnego kosztu: ' + koszt minimalny); Console.ReadLine(); } // Kod sterownika public static void Main(String[] args) { int V = 4; int E = 5; Wykres graph = new Graph(V, E); // dodaj krawędź 0-1 graph.edge[0].src = 0; graph.edge[0].dest = 1; graph.edge[0].waga = 10; // dodaj krawędź 0-2 graph.edge[1].src = 0; graph.edge[1].dest = 2; ; // dodaj krawędź 0-3 graph.edge[2].src = 0; graph.edge[2].dest = 3; graph.edge[2].weight = 5; // dodaj krawędź 1-3 wykresu. krawędź[3].src = 1; graph.edge[3].dest = 3; graph.edge[3].waga = 15; // dodaj krawędź 2-3 graph.edge[4].src = 2; .edge[4].dest = 3; graph.edge[4].weight = 4; // Wywołanie funkcji graph.KruskalMST(); } } // Ten kod jest autorstwa Aakasha Hasiji>

>

>

JavaScript




> // JavaScript implementation of the krushkal's algorithm.> > function> makeSet(parent,rank,n)> {> >for>(let i=0;i { parent[i]=i; rank[i]=0; } } function findParent(parent,component) { if(parent[component]==component) return component; return parent[component] = findParent(parent,parent[component]); } function unionSet(u, v, parent, rank,n) { //this function unions two set on the basis of rank //as shown below u=findParent(parent,u); v=findParent(parent,v); if(rank[u] { parent[u]=v; } else if(rank[u] { parent[v]=u; } else { parent[v]=u; rank[u]++;//since the rank increases if the ranks of two sets are same } } function kruskalAlgo(n, edge) { //First we sort the edge array in ascending order //so that we can access minimum distances/cost edge.sort((a, b)=>{zwróć a[2] - b[2]; }) //wbudowana funkcja szybkiego sortowania jest dostępna w stdlib.h //przejdź do https://www.techcodeview.com Sortowanie krawędzi zajmuje czas O(E * logE). Po sortowaniu iterujemy po wszystkich krawędziach i stosujemy algorytm find-union. Operacje find i union mogą zająć co najwyżej czas O(logV). Zatem ogólna złożoność to czas O(E * logE + E * logV). Wartość E może wynosić co najwyżej O(V2), więc O(logV) i O(logE) są takie same. Zatem całkowita złożoność czasowa wynosi O(E*logE) lub O(E*logV). Przestrzeń pomocnicza: O(V + E), gdzie V to liczba wierzchołków, a E to liczba krawędzi grafu. Ten artykuł został opracowany przez Aashish Barnwal i sprawdzony przez zespół techcodeview.com.>