超详细的Prim算法解析

 最小生成树之Prim 算法

  • 什么是最小生成树?

我的理解:最小生成树,在一个无向图中,生成树必须包含这个无向图的所有顶点,且顶点与顶点之间必须有路,且这些路径不能形成一个环。而最小生成树就是在所有生成树当中,所以路径的的花费加起来最小的那一颗生成树。

想要知道最小生成树更正确的概念,可看书或自行百度!

超详细的Prim算法解析_第1张图片

 

如图所示,该无向图又三颗生成树,而最小生成树就是第二颗,花费为9。

 

  • prim算法求最短路径的思想

先在图中找一个最开始出发的顶点,开始出发,把出发的顶点先放入遍历的顶点集中,然后找到从这个顶点出发所有路径中花费最小的那条路径,然后把该路径能到达的顶点也放入遍历的顶点集中,然后接着找顶点集中所有顶点能出发通往未遍历的顶点的路径中最短的那条路径,然后选择这条路径,再把该路径到达的顶点放入遍历顶点集中,重复以上操作,直到所有的顶点都已经遍历完毕,算法结束,返回最小的花费。

 

超详细的Prim算法解析_第2张图片

 

 

超详细的Prim算法解析_第3张图片

如上图的无向图中,用prim求最短路的步骤就是:

一:选择v0顶点为出发点,将v0顶点放入已经遍历的顶点集合中,图中标蓝的就是已经遍历的顶点。从V0出发的路径有:

  1. v0到v1,花费为6
  2. v0到v2,花费为1
  3. v0到v3,花费为5

所以第二条路径花费最小,就选择它,花费cost=1

二:此时集合中有顶点v0和顶点v2。

从顶点v0出发符合条件的路径有:

  1. v0到v1,花费为6
  2. v0到v3,花费为5

从顶点v2出发符合条件的路径有:

  1. v2到v1,花费为5
  2. v2到v3,花费为5
  3. v2到v4,花费为6
  4. v2到v5,花费为4

所以从v2出发的第四条路径花费最小,就选择它,花费cost=1+4=5

三:此时集合中有顶点v0、顶点v2、顶点v5。

从顶点v0出发符合条件的路径有:

  1. v0到v1,花费为6
  2. v0到v3,花费为5(构成环路、不能选)

从顶点v2出发符合条件的路径有:

  1. v2到v1,花费为5
  2. v2到v3,花费为5(构成环路、不能选)
  3. v2到v4,花费为6

从顶点v5出发符合条件的路径有:

  1. v5到v3,花费为2
  2. v5到v4,花费为6

所以从v5出发的第一条路径花费最小,选择它,花费cost=1+4+2=8。

四:此时集合中有顶点v0、顶点v2、顶点v5、顶点v3。

从顶点v0出发符合条件的路径有:

  1. v0到v1,花费为6

从顶点v2出发符合条件的路径有:

  1. v2到v1,花费为5
  2. v2到v4,花费为6

从顶点v5出发符合条件的路径有:

  1. v5到v4,花费为6

从顶点v3出发没有符合条件的路径

所以从v2出发到顶点v1的路径花费最小,cost=1+4+2+5=12。

五:此时集合中有顶点v0、顶点v2、顶点v5、顶点v3、顶点v1。

从顶点v1出发符合条件的路径有:

  1. v1到v4,花费为3

从顶点v2出发符合条件的路径有:

  1. v2到v4,花费为6

从顶点v5出发符合条件的路径有:

  1. v5到v4,花费为6

所有从顶点v1出发到顶点v4的路径花费最小,cost=1+4+2+5+3=15。

六:此时集合中有顶点v0、顶点v2、顶点v5、顶点v3、顶点v1、顶点v4,所有顶点都已经遍历完毕,算法结束,求得的最小生成树的最小花费是15。

 

三:如何用代码去实现prim算法呢?

首先,我们需要三个数组:selected数组,用来判断节点是否被访问过;parent数组,用来记录上一个节点,也就是说选的这条路径是由parent记录的节点通往当前节点的。minCost数组,用来记录上一个节点到该点的最小花费;

来,为了更好的理解,上图(以上面的讲解图为例):

(1)先对各个数组进行初始化,每个数组初始化的数据如下图

超详细的Prim算法解析_第4张图片

 

(2)以第一个顶点为出发点,此时v0已经被选择了,所以把minCost的数组改为不可比较的状态,这样下一次选择的顶点需要通过比较minCost数组的值来获取,这样就可以保证选的是最小花费的那条边且不会形成环路了。以后选择一个顶点,就得把该顶点下的minCost数组对应的值改为X(不可比较的状态)这样才能保证不出现环路的情况。

超详细的Prim算法解析_第5张图片

 

(3)继续根据上图找到minCost数组中最小的值,是1,它对应的顶点是V2,则选择这个顶点,然后更新minCost数组和parent数组。更新条件时当前获得的minCost的值比原来记录的值要小。例如:V1顶点对应的原本的值6(v0到v1的花费),但是v2到v1的花费是5,比6小,就更新它。

超详细的Prim算法解析_第6张图片

 

(4)重复上述操作,可得到以下图解:

超详细的Prim算法解析_第7张图片

超详细的Prim算法解析_第8张图片

超详细的Prim算法解析_第9张图片

超详细的Prim算法解析_第10张图片

所以顶点都已经选择完毕了,算法结束。我们可以通过parent来还原最小生成树啦。

首先找到为-1的结点,发生是V0,所以路径为V0->……,接下来,找parent值为0的结点,发现是V2,所以V0->V2的边被选择,然后再找parent值为2的结点,是V1和V5,因此V2->V1,V2->V5的边被选择。然后分别找parent值为1、5的结点,发现是V2、V3,因此V1->V2,V5->V3的边被选中。

实现的代码如下:

题目:

Description

给出一个无向网,求该无向网的最小生成树。各条边的权重不超过100000。

本题与下一题的测试数据是一样的,本题请用Prim算法来做,以便与下一题做比较。

Input

输入的第一行是一个整数N,表示该网的顶点个数。 3 ≤ N ≤ 100

接下来是N行,每行N个整数,表示每个顶点到其余顶点的距离。

Output

输出该最小生成树的权重。

Sample Input

4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0

Sample Output

28

/*prim算法求最小生成树
邻接矩阵法构造图 
算法思想:首先从顶点0出发,然后找到下一个距离顶点0最近的顶点i,也进入集合U
此算法是从进入了集合U的顶点中找到个各顶点与其他未进入集合U的顶点之间的一条最短的边去连接的 
*/
# include
#include
using namespace std;
int inf=0x3f3f3f3f;
int s[100][100];//存储图 

bool selected[100];//判断顶点是否被选中 
int minCost[100];//保存已选顶点中到该顶点的最小的路径 
int parent[100];//保存父节点 
 
void prim(int n,int k){
	//第一步,第K个顶点被选中 
	selected[k]=true;
	minCost[k]=-1;
	//开始更新minCost数组和parent数组 
	for(int i=0;i>n;
	for(int i=0;i>s[i][j];
			if(s[i][j]==0)
			s[i][j]=inf;
		}
		selected[i]=false; 
		minCost[i]=inf;
		parent[i]=-1;
	} 
	// cout<<"图输入完成!"; 
	 prim(n,0);
	 int sum=0;
	 for(int i=1;i

这里用的是递归的方法去做,递归的方式理解是好理解,但是运行时所有的时间也会长一些,因为它的时间复杂度稿,我提交通过后显示的时间是25Ms,用的时间还是比较长的。所以要想运行时间快一些,就得降低一下时间复杂度,用迭代的方式去做这道题目应该会用时少一些。

 

 

 

 

你可能感兴趣的:(算法)