【C语言】普里姆算法详解:计算最少成本和路径输出

【应用背景】连通图中各顶点所需要的最少成本。

(1)普里姆算法

1)根据需求构造返回类型,比如求最少成本,则定义返回类型为int型的函数,传入参数为邻接矩阵g,和出发点v0;

2)定义变量:lowcost[]数组(存储图中还未并入树的各顶点到当前生成树最短边的权值),vset[]数组(标记顶点是否被并入生成树中,1为真,0为假),循环变量i,j,整型变量min,k和sum,min表示生成树到剩余顶点最短边中最短的那条边的权值,k表示与这条边相连并将被并入生成树的顶点,sum表示成本总和;

3)做一个循环将lowcost数组初始化为与出发点相邻的各条边的权值,vset初始化为0;

4)将v0标记为1,表示生成树的根结点,接下来将剩余的n-1个顶点并入生成树中,即做n-1次循环:

  • 将min初始化为INF,无穷大。
  • 找出min值和k值。嵌套内循环1:执行n次,依次遍历每一个顶点,如果vset[j]为0,并且lowcost[j]
  • 计算成本,sum += min,并将k标记为1。
  • 以新并入的顶点k为媒介更新lowcost数组。因为当前生成树发生了变化,剩余顶点到生成树的权值也应发生改变。嵌套内循环2:执行n次,依次遍历每一个顶点,如果vset[i]为0,并且g.edges[k][j] < lowcost[j],表示顶点j未并入生成树,且与顶点k相连的边的权值要小于顶点j到旧的生成树最短边的权值。此时说明必须要更新lowcost[]数组,将该边权值赋值g.edges[k][j] 给lowcost[j]。

5)返回sum,算法结束。

【算法分析】算法嵌套两层循环,外层循环执行n-1次,内层循环1中if语句比较n次,所以算法时间复杂度为O(n^{2})。其中n为图中顶点个数,所以普里姆算法只与图的顶点数有关,顶点越少,时间复杂度越小,因此普里姆算法适合稠密图。

(2)核心代码:

int Prime(MGraph g, int v0)
{
	int lowcost[maxSize], vset[maxSize],vpath[maxSize];
	int i, j, k, min,sum = 0;

	for (i = 0; i < g.n; ++i)
	{
		lowcost[i] = g.edges[v0][i];
		vset[i] = 0;
		if (lowcost[i] < INF)
			vpath[i] = v0;
		else
			vpath[i] = -1;
	}

	vset[v0] = 1;

	for (i = 0; i < g.n - 1; ++i)
	{
		min = INF;
		for (j = 0; j < g.n; ++j)
			if (vset[j] == 0 && lowcost[j] < min)
			{
				min = lowcost[j];
				k = j;
			}
		sum += min;
		vset[k] = 1;
		printf("(%d,%d)", vpath[k], k);

		for (j = 0; j < g.n; ++j)
			if (vset[j] == 0 && g.edges[k][j] < lowcost[j])
			{
				lowcost[j] = g.edges[k][j];
				vpath[j] = k;//父节点为K
			}
	}
	return sum;
}

(3)验证算法正确性

数据来源:天勤树P193页例题

图样子:额,后期有时间更新

数据:

MGraph.txt

5
8
0 1 5
0 2 1
0 3 2
1 2 3
1 4 4 
2 4 2
2 3 6
3 4 3
01234

测试代码:

#include 
#include 
#define maxSize 50
#define INF 65535

typedef struct
{
	int num;
	char info;
}VertexType;

typedef struct
{
	int edges[maxSize][maxSize];
	int n, e;//顶点数,和边数
	VertexType vex[maxSize];//存放结点信息
}MGraph;

MGraph G;
int visit[maxSize];
FILE* fp;
void BuildGraph()
{
	int i, j;
	int v1, v2, w;
	fp = fopen("C:\\CodeBlocksProject\\MGraph.txt", "r");
	//Create Graph
	fscanf(fp, "%d", &G.n);
	for (i = 0; i < G.n; ++i)
		for (j = 0; j < G.n; j++)
			G.edges[i][j] = INF;//若为有向边则赋值为INF

	fscanf(fp, "%d", &G.e);
	for (i = 0; i < G.e; i++)
	{
		fscanf(fp, "%d%d%d", &v1, &v2, &w);
		//Insert Edges
		G.edges[v1][v2] = w;
		G.edges[v2][v1] = w;//若为无向边,则对称位置权值相等
	}

	/*顶点有数据的话,则读入数据*/
	fgetc(fp);//消化输入的换行符
	for (i = 0; i < G.n; i++)
	{
		G.vex[i].num = i;//顶点编号
		//getchar();//消化输入的换行符
		fscanf(fp, "%c", &G.vex[i].info);//顶点其他信息
	}

}


int Prime(MGraph g, int v0)
{
	int lowcost[maxSize], vset[maxSize],vpath[maxSize];
	int i, j, k, min,sum = 0;

	for (i = 0; i < g.n; ++i)
	{
		lowcost[i] = g.edges[v0][i];
		vset[i] = 0;
		if (lowcost[i] < INF)
			vpath[i] = v0;
		else
			vpath[i] = -1;
	}

	vset[v0] = 1;

	for (i = 0; i < g.n - 1; ++i)
	{
		min = INF;
		for (j = 0; j < g.n; ++j)
			if (vset[j] == 0 && lowcost[j] < min)
			{
				min = lowcost[j];
				k = j;
			}
		sum += min;
		vset[k] = 1;
		printf("(%d,%d)", vpath[k], k);

		for (j = 0; j < g.n; ++j)
			if (vset[j] == 0 && g.edges[k][j] < lowcost[j])
			{
				lowcost[j] = g.edges[k][j];
				vpath[j] = k;
			}
	}
	return sum;
}


int main()
{
	int sum;

	BuildGraph();

	printf("Path:");

	sum = Prime(G, 0);

	printf("\nSum:%d\n", sum);

	system("pause");
}

测试结果(Perfect!):

【C语言】普里姆算法详解:计算最少成本和路径输出_第1张图片

 

你可能感兴趣的:(初试数据结构学习)