下面是 Kruskal 算法实现最小生成树
(1)下面是有权无向图,初始边的连接如下图
(2)对上图的权重按照从小到大排序,如下图
(3)按照权重从小到大寻找边,只要不形成环该权重边就符合要求,如下图前5个标为红色的都没有形成环,而边1-3会形成环,舍弃,1-5也会形成环,舍弃,4-5不会形成环,保留,边2-6不会形成环,保留,下图标红的形成最小生成树
使用堆实现权重排序
#include <iostream>
#include <iomanip>
#include <vector>
#include <ctime>
#include <string>
#include "DenseGraph.h"
#include "SparseGraph.h"
#include "ReadGraph.h"
#include "LazyPrimMST.h"
#include "PrimMST.h"
#include "KruskalMST.h"
using namespace std;
int main() {
string filename1 = "testG1.txt";
int V1 = 8;
string filename2 = "testG2.txt";
int V2 = 250;
string filename3 = "testG3.txt";
int V3 = 1000;
string filename4 = "testG4.txt";
int V4 = 10000;
SparseGraph<double> g1 = SparseGraph<double>(V1, false);
ReadGraph<SparseGraph<double>, double> readGraph1(g1, filename1);
cout<<filename1<<" load successfully."<<endl;
SparseGraph<double> g2 = SparseGraph<double>(V2, false);
ReadGraph<SparseGraph<double>,double> readGraph2(g2, filename2);
cout<<filename2<<" load successfully."<<endl;
SparseGraph<double> g3 = SparseGraph<double>(V3, false);
ReadGraph<SparseGraph<double>,double> readGraph3(g3, filename3);
cout<<filename3<<" load successfully."<<endl;
SparseGraph<double> g4 = SparseGraph<double>(V4, false);
ReadGraph<SparseGraph<double>,double> readGraph4(g4, filename4);
cout<<filename4<<" load successfully."<<endl;
cout<<endl;
clock_t startTime, endTime;
// Test Lazy Prim MST
cout<<"Test Lazy Prim MST:"<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST1(g1);
endTime = clock();
cout<<"Test for G1: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST2(g2);
endTime = clock();
cout<<"Test for G2: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST3(g3);
endTime = clock();
cout<<"Test for G3: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST4(g4);
endTime = clock();
cout<<"Test for G4: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
cout<<endl;
// Test Prim MST
cout<<"Test Prim MST:"<<endl;
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST1(g1);
endTime = clock();
cout<<"Test for G1: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST2(g2);
endTime = clock();
cout<<"Test for G2: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST3(g3);
endTime = clock();
cout<<"Test for G3: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
SparseGraph<double> g5 = SparseGraph<double>(V4, false);
ReadGraph<SparseGraph<double>,double> readGraph5(g5, filename4);
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST4(g5);
endTime = clock();
cout<<"Test for G4: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
cout<<endl;
// Test Kruskal MST
cout<<"Test Kruskal MST:"<<endl;
startTime = clock();
KruskalMST<SparseGraph<double>, double> KruskalMST1(g1);
endTime = clock();
cout<<"Test for G1: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
KruskalMST<SparseGraph<double>, double> KruskalMST2(g2);
endTime = clock();
cout<<"Test for G2: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
KruskalMST<SparseGraph<double>, double> KruskalMST3(g3);
endTime = clock();
cout<<"Test for G3: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
KruskalMST<SparseGraph<double>, double> KruskalMST4(g4);
endTime = clock();
cout<<"Test for G4: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
return 0;
}
KruskalMST.h
#include <iostream>
#include <vector>
#include "UF.h"
#ifndef _EDGE_H_
#define _EDGE_H_
#include "Edge.h"
#endif
#ifndef _MINHEAP_H_
#define _MINHEAP_H_
#include "MinHeap.h"
#endif
using namespace std;
template <typename Graph, typename Weight>
class KruskalMST{
private:
vector< Edge<Weight> > mst;
Weight mstWeight;
public:
KruskalMST(Graph &graph){
MinHeap< Edge<Weight> > pq( graph.E() );
for ( int i = 0; i < graph.V(); i++ ){
typename Graph::adjIterator adj(graph, i);
for ( Edge<Weight> *e = adj.begin(); !adj.end(); e = adj.next() )
if ( e->v() < e->w() )
pq.insert(*e);
}
UnionFind uf = UnionFind(graph.V());
while( !pq.isEmpty() && mst.size() < graph.V() - 1){
Edge<Weight> e = pq.extractMin();
if (uf.isConnected( e.v(), e.w() ))
continue;
mst.push_back(e);
uf.unionElements( e.v(),e.w() );
}
mstWeight = mst[0].wt();
for( int i = 1; i < mst.size(); i++ )
mstWeight += mst[i].wt();
}
~KruskalMST(){
}
vector< Edge<Weight> > mstEdges(){
return mst;
}
Weight result(){
return mstWeight;
}
};
UF.h
#include <iostream>
#include <cassert>
using namespace std;
// Quick Union + rank + path compression
class UnionFind{
private:
int* parent;
int* rank;
int count;
public:
UnionFind(int count){
parent = new int[count];
rank = new int[count];
this->count = count;
for( int i = 0 ; i < count ; i ++ ){
parent[i] = i;
rank[i] = 1;
}
}
~UnionFind(){
delete[] parent;
delete[] rank;
}
int size(){
return count;
}
bool isConnected( int p , int q ){
return find(p) == find(q);
}
int find(int p){
assert( p >= 0 && p < count );
// path compression 1
while( p != parent[p] ){
parent[p] = parent[parent[p]];
p = parent[p];
}
return p;
}
void unionElements(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if( pRoot == qRoot )
return;
if( rank[pRoot] < rank[qRoot] )
parent[pRoot] = qRoot;
else if( rank[qRoot] < rank[pRoot])
parent[qRoot] = pRoot;
else{ // rank[pRoot] == rank[qRoot]
parent[pRoot] = qRoot;
rank[qRoot] ++;
}
}
void show(){
for( int i = 0 ; i < count ; i ++ )
cout<<i<<" : "<<parent[i]<<endl;
}
};
输出为
可以看出 Kruskal 的运行时间要比 Prim 慢,但是由于Kruskal算法简单,容易实现,对于比较小的图比较适用