算法思想:假设N = { V, {E} }是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图
T = { V, {} },图中每个顶点自成一个连通分量。在E中选择 代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直到T中所有顶点都在同一连通分量上为止。
T = { V, {} },图中每个顶点自成一个连通分量。在E中选择 代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直到T中所有顶点都在同一连通分量上为止。
此算法的 FindInUFS函数由边数e决定,时间复杂度为O(loge),而外面有一个for循环e次,所以Kruskal算法的时间复杂度为O(eloge)。
对比Prim算法与Kruskal算法,Kruskal算法主要针对边来展开,边数少时效率会很高,所以对稀疏图有很大的优势。而Prim算法对于稠密图,即边数非常多的情况会更好一些。
#include <iostream>
#include <string>
#include <queue>
#include <deque>
using namespace std;
#define MAXSIZE 10 //顶点最大个数
typedef string VertexType; //顶点类型
typedef int EdgeType; //权值类型,有向图(0,1),无向图(权值,无穷大)
#define INFINITY 0xffff
typedef struct
{
VertexType Vexs[MAXSIZE]; //顶点向量
EdgeType arcs[MAXSIZE][MAXSIZE]; //邻接矩阵,可看作为边表
int iVexNum; //顶点个数
int iArcNum; //边数
}MGraph;
#define SUCCESS 1
#define UNSUCCESS 0
typedef int Status;
//由顶点值得到顶点索引
int GetIndexByVertexVal( const MGraph& MG, VertexType val )
{
for ( int i = 0; i < MG.iVexNum; ++i )
{
if ( val == MG.Vexs[i] )
return i;
}
return -1;
}
//创建无向图
Status CreateGraph( MGraph& MG )
{
cout << "输入顶点个数以及边数:";
cin >> MG.iVexNum >> MG.iArcNum;
cout << "请输入" << MG.iVexNum << "个顶点:";
for ( int i = 0; i < MG.iVexNum; ++i )
{
cin >> MG.Vexs[i];
}
for ( int i = 0; i < MG.iVexNum; ++i )
{
for ( int j = 0; j < MG.iVexNum; ++j )
{
MG.arcs[i][j] = INFINITY;
}
}
cout << "请输入由两点构成的边及其权值:";
for ( int i = 0; i < MG.iArcNum; ++i )
{
VertexType first;
VertexType second;
EdgeType weight;
cin >> first >> second >> weight;
int m = GetIndexByVertexVal( MG, first );
int n = GetIndexByVertexVal( MG, second );
if ( m == -1 || n == -1 )
return UNSUCCESS;
MG.arcs[m][n] = MG.arcs[n][m] = weight;
}
return SUCCESS;
}
typedef struct
{
int begin;
int end;
int weight; //权值
}Edge;
//在并查集中查找
int FindInUFS( int* parent, int k )
{
while ( parent[k] != k )
{
k = parent[k];
}
return k;
}
//克鲁斯卡尔算法求最小生成树
void MiniSpanTree_Kruskal( const MGraph& G )
{
//转换为边集数组
Edge* pEdge = new Edge[G.iArcNum];
memset( pEdge, 0, G.iArcNum*sizeof(Edge) );
int m = 0;
for ( int i = 1; i < G.iVexNum; ++i )
{
int k = i - 1;
for ( int j = 0; j <= k; ++j )
{
if ( G.arcs[i][j] != INFINITY )
{
pEdge[m].begin = i;
pEdge[m].end = j;
pEdge[m].weight = G.arcs[i][j];
++m;
}
}
}
//按权值从小到大进行排序
for ( int i = 1; i < G.iArcNum-1; ++i )
{
bool flag = false;
for ( int j = 0; j < G.iArcNum - i; ++j )
{
if ( pEdge[j].weight > pEdge[j+1].weight )
{
int temp;
temp = pEdge[j].begin;
pEdge[j].begin = pEdge[j+1].begin;
pEdge[j+1].begin = temp;
temp = pEdge[j].end;
pEdge[j].end = pEdge[j+1].end;
pEdge[j+1].end = temp;
temp = pEdge[j].weight;
pEdge[j].weight = pEdge[j+1].weight;
pEdge[j+1].weight = temp;
flag = true;
}
}
if ( !flag )
{
break;
}
}
//并查集
int* parent = new int[G.iArcNum];
for ( int i = 0; i < G.iArcNum; ++i )
{
parent[i] = i;
}
for ( int i = 0; i < G.iArcNum; ++i )
{
int m = FindInUFS( parent, pEdge[i].begin );
int n = FindInUFS( parent, pEdge[i].end );
if ( m != n )
{
cout << "(" << pEdge[i].begin << "," << pEdge[i].end
<< "," << pEdge[i].weight << ")" << endl;
parent[m] = n;
}
}
if ( !pEdge )
{
delete[] pEdge;
pEdge = NULL;
}
if ( !parent )
{
delete[] parent;
parent = NULL;
}
}
int main()
{
MGraph MG;
CreateGraph( MG );
//克鲁斯卡尔算法求最小生成树
cout << "普里姆算法求解最小生成数:" << endl;
MiniSpanTree_Kruskal( MG );
cout << endl;
return 0;
}