第一次写CSDN,不太熟悉。
#include<climits>//①
#include<vector>
//定义无穷大
const int INF = INT_MAX;
//Prim
void Prim(std::vector<std::vector<int>>&graph,int start){
int V = graph.size();//顶点数量
std::vector<bool>inMST(V,false);//判断是否在最小生成树中
std::vector<int>key(V,INF);//key[i]表示从start到i的最小距离
std::vector<int>parent(V,-1);//parent[i]表示生成树中i的父节点
key[start] = 0;
//有V个边所以遍历V-1次
for(int count = 0;count < V-1;++count){
//选择不在MST中且key值最小的点u
int u = -1; //②
for(int v = 0;v<V;v++){
if(!inMST[v]&&((u==-1)||key[v]<key[u])){
u = v;
}
}
//确定了预加入点,最后入MST
inMST[u] = true;
//每次加入一个新点,就要更新与之相邻顶点的key值
for(int v = 0;v<V;v++){
if(graph[u][v]!=0&&!inMST[v]&&graph[u][v]<key[v]){
key[v] = graph[u][v];
parent[v] = u;
}
}
}
// 打印最小生成树
for (int i = 0; i < V; ++i) {
if(i==start)//③
continue;
std::cout << parent[i] << " -- " << i << " == " << graph[parent[i]][i] << std::endl;
}
}
int main()
{
std::vector<std::vector<int>> graph = {
{0, 2, 0, 6, 0},
{2, 0, 3, 8, 5},
{0, 3, 0, 0, 7},
{6, 8, 0, 0, 9},
{0, 5, 7, 9, 0}
};
Prim(graph,3);
return 0;
}
其他的部分均容易理解,重点介绍一下代码中划线部分
①#include<climits>
这个头文件中包含了一些常用的最大值的定义。
INT_MAX : int 类型的最大值。
INT_MIN : int 类型的最小值。
UINT_MAX : unsigned int 类型的最大值。
LONG_MAX : long int 类型的最大值。
LONG_MIN : long int 类型的最小值。
LLONG_MAX : long long int 类型的最大值。
LLONG_MIN : long long int 类型的最小值。
②int u = -1;
目的:
1.0 刚开始MST没有点,循环从v = 0开始。由于u = -1,满足if条件,毋庸置疑,一开始u一定是等于0的。那么之后呢,由于开始我们给所有key值均INT_MAX,而我们上面设置的key[start]=0,,会使得start点的key最小,当循环v = start那么就符key[start]<key[u],u则更新为start,且其为目前的最小,循环结束后,start点也就顺利地第一个进入了MST。
2.0 外侧循环每进行一次,就会使u==-1,那么由判断里的u==-1必定会使一个顶点预加入MST(确定了点,但是还没进入MST),这样就可以实现每次添加点的效果。同时,这个点是按照顺序从前到后预加入的,但不一定是符合最小值的,那么内层循环接下来就会比较key值,就类似于冒泡排序,先确定一个key[u],再接下来去和其他的key[v]比较,如何有key[v]比key[u]小,那么预加入点u就更新为了v。最后循环结束,最小key的点一定找出来了,加入MST。
③if(i==start) continue;
因为start点是第一个进入MST中的,他就是根,所以parent[start] = -1;最后输出其没有意义。