Kruskal算法的基本过程:任给一个有n个顶点的连通网络N={V,E},首先构造一个由这n个顶点组成、不含任何边的图T={V,},其中每个顶点自成一个连通分量。不断从E中取出权值最小的一条边(若有多条,任取其一),若该边的两个端点来自不同的连通分量,则将此边加入到T中。如此重复,直到所有顶点在同一个连通分量上为止。
实现分析:
- 由于需要不断判断两个端点是否来自同一个连通分量,否的话,两个连通分量的顶点并起来,这里可以用并查集实现。
- 由于不断从边集中取出权值最小的一条边,这里可以用最小堆来实现。
下面提供面向邻接矩阵和面向邻接表的Kruskal算法实现:
//
// main.cpp
// Kruskal-最小生成树
//
// Created by scnulpc on 2018/10/25.
// Copyright © 2018年 scnulpc. All rights reserved.
//
#include <iostream>
#include <queue>
#define maxWeight 99
#define size 100
using namespace std;
//邻接矩阵的存储结构
struct Edge {
int v1,v2;
int weight;
Edge(int a,int b,int cost):v1(a),v2(b),weight(cost){}
friend bool operator < (Edge a,Edge b)
{
return a.weight > b.weight; //权重小的优先级高,构造最小堆
}
};
int map[size][size];
//邻接表的存储结构
struct edge
{
int dest;
int weight;
edge* link;
edge(int a,int b):dest(a),weight(b),link(NULL){}
};
struct VertexNode
{
int v;
edge* adjacent;
VertexNode(int a):v(a),adjacent(NULL){}
};
VertexNode* vertecies[size];
int Find(int x,int *parent)
{
if (parent[x]<0) return x;
else return Find(parent[x], parent);
}
bool Union(int v1,int v2,int *parent)
{
int num1 = Find(v1, parent);
int num2 = Find(v2, parent);
if (num1==num2&&num1!=-1) return false;
if (num1<num2) {
parent[num1] += parent[num2];
parent[num2] = num1;
}
else
{
parent[num2]+=parent[num1];
parent[num1] = num2;
}
return true;
}
//基于邻接矩阵的有权无向图的Kruskal算法
void Kruskal(int n)
{
priority_queue<Edge> q;
int UFSet[n];
for (int i=0; i<n; i++)
{UFSet[i]=-1;}
for (int i=0; i<n; i++)
{
for (int j=i+1; j<n; j++) {
if (map[i][j]>0&&map[i][j]<maxWeight)
{
q.push(*(new Edge(i,j,map[i][j])));
}
}
} //上面都是准备工作,初始化并查集,所有边入最小堆
int vertex = 1;
while (vertex!=n&&!q.empty())
{
Edge top = q.top();q.pop();
if(Union(top.v1, top.v2, UFSet)) vertex++;
}
if (vertex==n) cout<<"最小生成树已生成"<<endl; //这个判断条件也可以为 是否存在这样的n-1条边,把n个顶点连接起来
else cout<<"不存在最小生成树"<<endl;
}
//基于邻接表有权无向图(有向图)的Kruskal算法
void Kruskal1(int n)
{
priority_queue<Edge> q;
int UFSet[n];
for (int i=0; i<n; i++)
UFSet[i]=-1;
for (int i=0; i<n; i++)
{
edge* p = vertecies[i]->adjacent;
while (p!=NULL) {
q.push(*(new Edge(i,p->dest,p->weight))); //如果为无向图,这里可以改进,判断边的重复,进一半的边
p=p->link;
}
}
int numEdge = 0;
int count = 1;
while (count!=n&&!q.empty())
{
Edge temp = q.top();q.pop();
if (Union(temp.v1, temp.v2, UFSet)) {
numEdge++;
}
}
if (numEdge==n-1) {
cout<<"最小生成树存在"<<endl;
}
else
{
cout<<"最小生成树不存在"<<endl;
}
}
/*
测试数据
0 1 99 3 99
1 0 2 4 99
99 2 0 99 99
3 99 99 0 5
99 99 99 5 0
*/
int main(int argc, const char * argv[])
{
int n=5;
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) {
cin>>map[i][j];
}
}
Kruskal(n);
for (int i=0; i<n; i++) {
vertecies[i] = new VertexNode(i);
}
vertecies[0]->adjacent = new edge(1,1);
vertecies[0]->adjacent->link = new edge(3,3);
vertecies[1]->adjacent = new edge(0,1);
vertecies[1]->adjacent->link = new edge(2,2);
vertecies[1]->adjacent->link->link = new edge(3,4);
vertecies[2]->adjacent = new edge(1,2);
vertecies[3]->adjacent = new edge(0,3);
vertecies[3]->adjacent->link = new edge(4,5);
vertecies[4]->adjacent = new edge(3,5);
Kruskal1(n);
return 0;
}