C++实现的Prim算法(最小生成树)

1. 最小生成树

是一个在给定的无向图G(V,E)中求出一棵树T,使得这棵树拥有图G中的所有有顶点,且所有边都是来自于图G中的边,并且满足这棵树的边权之和是最小的

对于最小生成树需要掌握以下三个性质:

① 最小生成树是树,因此其边数是等于顶点数减1,而且树内一定不会有环

② 对于给定的图G(V,E)其最小生成树可以是不唯一的,但是边权这个是唯一的

③ 由于最小生成树是在无向图中产生的,所以其根节点可以是这棵树上的任意一个节点,于是题目中涉及了最小生成树的输出为了让最小生成树是唯一的,一般都是直接给出根节点

求解最小生成树的算法有两种,即Kruskal算法与prim算法,这两个算法都是采用了贪心的策略,只是贪心的策略是不一样

2. 下面使用的是prim算法求解最小生成树的过程,prim算法与Dijkstra算法是很类似的,只是有一个地方存在差别,就是d[]数组记录的含义是不一样的,Dijkstra算法中的是源点到其余顶点的最短距离,而这里的是顶点vi到集合S之间的距离

下面是算法的执行过程:

① 每次都是从集合V-S中选择与集合S最近的一个顶点,集合S中的顶点存放的是已经被访问过的顶点,将这个顶点记录为u,访问u并且将其加入到集合S中,同时将这条离集合S的边加入到生成树中

② 令顶点u作为集合S与集合V-S连接的接口,优化从u能够到未访问顶点与集合S的最短距离

其实对于上面的算法,我自己的理解是是否需要更新到当前顶点的距离,每一次都是选择d[]中最小的而且未被访问过的顶点,然后决定是否更新到其余顶点的距离,所以d数组的含义也可以这样理解,其他顶点到当前我这个顶点的最短距离是多少

3. 下面是C++代码写的代码:

#include
#include
using namespace std;
const int maxSize = 1000;
const int INF = 2147483647;
int n, m, G[maxSize][maxSize];
int d[maxSize];
bool vis[maxSize] = {false};
 
int prim(){
	fill(d, d + maxSize, INF);
	d[0] = 0;
	int ans = 0;
	for(int i = 0; i < n; i++){
		int u = -1, min = INF;
		for(int j = 0; j < n; j++){
			if(vis[j] == false && d[j] < min){
				u = j;
				min = d[j];
			}
		}
        //找不到小于INF的d[u]则剩下的顶点和集合s不连通
		if(u == -1) return -1;
		vis[u] = true;
		ans += d[u];
		for(int v = 0; v < n; v++){
			if(vis[v] == false && G[u][v] != INF && G[u][v] < d[v]){
				d[v] = G[u][v];
			}
		}
	}	
	return ans;
}
 
int main(void){
	int u, v, w;
	//n为顶点数, m为边数 
	scanf("%d%d", &n, &m);
	fill(G[0], G[0] + maxSize * maxSize, INF);
	for(int i = 0; i < m; i++){
		scanf("%d%d%d", &u, &v, &w);
		G[u][v] = G[v][u] = w;
	}
	int ans = prim();
	printf("%d\n", ans);
	for(int i = 0; i < n; ++i){
		cout << d[i] << " ";
	}
	cout << endl;
}

 

你可能感兴趣的:(图,C&&C++)