最小生成树——普里姆算法(Prim算法)

普里姆算法:

普里姆算法在找最小生成树时,将顶点分为两类,一类是在查找的过程中已经包含在树中的(假设为 A 类),剩下的是另一类(假设为 B 类)。

对于给定的连通网,起始状态全部顶点都归为 B 类。在找最小生成树时,选定任意一个顶点作为起始点,并将之从 B 类移至 A 类;然后找出 B 类中到 A 类中的顶点之间权值最小的顶点,将之从 B 类移至 A 类,如此重复,直到 B 类中没有顶点为止。所走过的顶点和边就是该连通图的最小生成树。
关于普里姆算法详细介绍可以看以下视频:
普里姆算法非常详细

算法思路

以下无向图为例:
最小生成树——普里姆算法(Prim算法)_第1张图片

1. 建立邻接矩阵

首先建立二维邻接矩阵arcs,两点之间如果连通则arcs[i] [j] = 权值,如果不连通则设置为一个很大的值自己设定以下用M表示。建立的邻接矩阵如下:

最小生成树——普里姆算法(Prim算法)_第2张图片
2.建立辅助数组
建立一个结构体数组,结构体中包含权值最小边的起始点和其对应的最小权值,这个数组在每一次找边的时候都会用到,因此需要设为全局变量。
closedge theclose 具体用法后面介绍

typedef struct {
	VertexType adjvex;//记录权值最小的边的起始点
	VRType lowcost;//记录该边的权值
}closedge[MAX_VERtEX_NUM]
3.开始找边

我们一上图的A为第一个点,开始。
首先将点A到各个点的权值存储在辅助数组theclose中
最小生成树——普里姆算法(Prim算法)_第3张图片
以上是theclose数组的信息,用数组下标来表示边的另一个点,以第二行为例表示边(0,1)的权值为6,即AB的权值为6。M设置为一个很大的int数表示两点不连通。权值0表示该点已经是生成树上的点了。

根据建立的theclose找到权值最小的点,设其下标为i(以上图为例i = 2,对应权值1,对应点C,0表示已经在树上了不参与最小点比较),然后修改其对应权值为0。此时表示i对应的点也加入到了树中,因此需要更新新加入的点i,到其他点的权值是否更小。只需要将下标i对应的点到各个点的权值Q与theclose中的权值q进行比对,因为0表示已经在树中的点了,所以只需要q>0&&Q 更新后的theclose信息如下
最小生成树——普里姆算法(Prim算法)_第4张图片
循环这个过程,找权值最小的点,更新权值。以上图为例更新后的信息如下
最小生成树——普里姆算法(Prim算法)_第5张图片

void miniSpanTreePrim(MGraph G, VertexType u){
	//找到该起始点在顶点数组中的位置下标
	int k = LocateVex(G, u);
	//首先将与该起始点相关的所有边的信息:边的起始点和权值,存入辅助数组中相应的位置,
	//例如(1,2)边,adjvex为0,lowcost为6,存入theclose[1]中,辅助数组的下标表示该边的顶点2
	for (int i = 0; i<G.vexnum; i++) {
		if (i != k) {
			theclose[i].adjvex = k;
			theclose[i].lowcost = G.arcs[k][i].adj;//权值 
			//原先两点之间没有联通 用0表示 现在用INFINITY
		}
	}
	//由于起始点已经归为最小生成树,所以辅助数组对应位置的权值为0,这样,遍历时就不会被选中
	theclose[k].lowcost = 0;
	//选择下一个点,并更新辅助数组中的信息
	for (int i = 1; i<G.vexnum; i++) {//i = 1? 这里的i只是表示循环次数 因为已经找到了一个点k所以
		//总次数-1 ,i = 1;             
		//找出权值最小的边所在数组下标
		k = minimun(G, theclose);
		//输出选择的路径
		printf("v%d v%d\n", G.vexs[theclose[k].adjvex], G.vexs[k]);//注意这里的k发生了变化 
		//归入最小生成树的顶点的辅助数组中的权值设为0
		theclose[k].lowcost = 0;
		//信息辅助数组中存储的信息,由于此时树中新加入了一个顶点,需要判断,由此顶点出发,
		//到达其它各顶点的权值是否比之前记录的权值还要小,如果还小,则更新
		//两点 1.如新的点与之前的点都能到达同一点的 更新最小权值
		//2.加入新连接的点能到达的点的信息
		for (int j = 0; j<G.vexnum; j++) {
			if (G.arcs[k][j].adj<theclose[j].lowcost) {
				//这里的0表示已经找过的点 ,INFINITY表示不同的点
				theclose[j].adjvex = k;
				theclose[j].lowcost = G.arcs[k][j].adj;
			}
		}
	}
	printf("\n");
}

依次以上循环直到找到所有的点,因为每次更新theclose信息的时候都会遍历其到各点的权值,所以时间复杂度为n^2,普利姆算法适合密图。

你可能感兴趣的:(数据结构,算法)