一、Kruskal算法
本质上属于一个贪心算法,在这个算法中,集合A是一个森林,其结点就是给定图的结点。每次加入到集合A的安全边永远是权重最小的连接两个不同分量的边。
MST-KRUSKAL(G,w)
A = ∅
for each vertex v ∈ G
MAKE-SET(v) //v个定点创建v棵树
sort the edges of G.E into nodecreasing order by weight w //递增排列不同权重的边
for each edge(u,v) ∈ G.E, taken in nodecreasing order by weight //通过递增顺序找权重最小边
if FIND-SET(u) != FIND-SET(v)
A = A ∪ {(u,v)} //将这条边加入到这个森林中
UNION(u,v) //对两棵树进行合并
return A
Kruskal C++代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
# define MAXN 100 //最大顶点数
# define MAXM 100 //最大边数
struct Edge
{
//(u,v)无向边,边权为w
int u;
int v;
int w;
Edge(int x , int y, int z) :u(x), v(y), w(z){}
};
vector<Edge> vec;//建立一个储存边的数组
void addEdge(int u,int v,int w)
{
vec.push_back(Edge(u, v, w));
}
//边的比较函数
bool cmp(Edge a, Edge b)
{
return a.w < b.w; //目的是根据权重递增排列
}
//并查集查找函数(查找根结点)
int parent[MAXN];
int find(int x)
{
return x == parent[x] ? x : parent[x] = find(parent[x]);
}
//Kruskal算法
int Kruskal(int n,int m,vector<Edge>&E)
{
for (int i = 0; i < m; i++) {
parent[i] = i;
}
int sum = 0;//权重和
sort(E.begin(), E.end(), cmp);//将边按照权重排序
int NumEdge = 0; //记录最小生成树边数
//并查集查找
for (int i = 0; i < vec.size(); i++) {
int ParentU = find(vec[i].u);
int ParentV = find(vec[i].v);
if (ParentU != ParentV) {
parent[ParentU] = ParentV;
sum += vec[i].w;
NumEdge++; //当前生成树边数加1
if (NumEdge == m - 1) //边数等于顶点数减1,算法结束
break;
}
}
if (NumEdge != m - 1) //无法连通时返回-1
return -1;
else
return sum; //返回最小生成树边权之和
}
int main()
{
int n,m;
cout << "请输入边数和点数:" << endl;
cin >> n >> m;
int x, y, z;
for (int i = 0; i < n; i++) {
cout << "请输入第"<<i<<"条边:"<<endl;
cin >> x >> y >> z;
addEdge(x, y, z);
}
int ans = Kruskal(n,m,vec);
cout << "result:"<<ans << endl;
return 0;
}
二、Prim算法
和Kruskal算法不同的是,Prim算法的性质是集合A的边总是会构成一棵树。算法的每一步在连接集合A和A之外的结点的所有边中,选一条轻量级边加入到A中,其本质上也是贪心策略,因为每一步加入的边必须让总权重的增加值最小。
伪代码:v.key保存的是连接v和树中结点所有边中最小边的权重;所有不在树A中的结点都存放在一个基于key属性的最小优先队列Q中。
MST-PRIM(G,w,r)
for each u ∈ G.V //将G中每一个结点初始化
u.key = ∞
u.π = NIL
r.key = 0 //从r开始遍历
Q = G.V //初始化最小优先队列
while Q != ∅
u = EXTRACT-MIN(Q) //在Q中找到结点u,为某条横跨切割(V-Q、Q)的轻量级边的一个端点。删除以后加入V-Q
//去u的邻接链表里寻找
for each v ∈ G.Adj[u]
//如果v不在树中且(u,v)权重小于v.key这个记录,那么这个点的父结点就是u
if v ∈ Q and w(u,v)<v.key
v.π = u
v.key = w(u,v)
Prim C++代码
#include <iostream>
#include <vector>
using namespace std;
char vetex[] = {'A', 'B', 'C', 'D', 'E', 'F'};
//顶点结构体
struct Node
{
int data;
int lowest_cost;
} closeEdge[6]; //设置一共有六个顶点
//原始图的结构体
struct Edge
{
//属性包括两个端点和相应的权重
int u;
int v;
int cost;
};
//邻接矩阵(直接把数字写好了)
void AdjMatrix( int adjMat[][6]) //邻接矩阵表示法
{
for (int i = 0; i < 6; i++) //初始化邻接矩阵
for (int j = 0; j < 6; j++)
{
adjMat[i][j] = INT_MAX; //初始化为正无穷
}
//对称矩阵,一共十条边二十个元素
adjMat[0][1] = 6; adjMat[0][2] = 1; adjMat[0][3] = 5;
adjMat[1][0] = 6; adjMat[1][2] = 5; adjMat[1][4] = 3;
adjMat[2][0] = 1; adjMat[2][1] = 5; adjMat[2][3] = 5; adjMat[2][4] = 6; adjMat[2][5] = 4;
adjMat[3][0] = 5; adjMat[3][2] = 5; adjMat[3][5] = 2;
adjMat[4][1] = 3; adjMat[4][2] = 6; adjMat[4][5] = 6;
adjMat[5][2] = 4; adjMat[5][3] = 2; adjMat[5][4] = 6;
}
//返回最小的边
int Minimum(Node*closeEdge)
{
int min = INT_MAX;
int index = -1;
for (int i = 0; i < 6; i++) {
if (closeEdge[i].lowest_cost < min && closeEdge[i].lowest_cost != 0) {
min = closeEdge[i].lowest_cost;
index = i;
}
}
return index;
}
//prim方法
void Prim(int adjMat[][6], int s)
{
//初始化顶点数组的lowest_cost
for (int i = 0; i < 6; i++) {
closeEdge[i].lowest_cost = INT_MAX;
}
closeEdge[s].data = s;//从顶点s开始,比如0
closeEdge[s].lowest_cost = 0;
//初始化辅助数组
for (int i = 0; i < 6; i++) {
//当i不为根结点
if (i != s)
{
closeEdge[i].data = s; //比如closeEdge[1].data = 1
closeEdge[i].lowest_cost = adjMat[s][i]; //closeEdge[i].lowest_cost = adjMat[0][1]
}
}
for (int e = 1; e <= 5; e++) {
int k = Minimum(closeEdge);
cout << vetex[closeEdge[k].data] << "--" << vetex[k] << endl;//加入到最小生成树
//代价为0代表closeEdge[k]已经用过了,因此在Minimum中不会再考虑closeEdge[k]
closeEdge[k].lowest_cost = 0;
closeEdge[k].lowest_cost = 0;//代价为0
for (int i = 0; i < 6; i++) {
if (adjMat[k][i] < closeEdge[i].lowest_cost) {
closeEdge[i].data = k;
closeEdge[i].lowest_cost = adjMat[k][i];
}
}
}
}
int main()
{
int adjMat[6][6] = { 0 };
AdjMatrix(adjMat);//邻接矩阵
cout << "Prim:" << endl;
Prim(adjMat, 0);
return 0;
}