关于图的prime和kruskal算法

作者原创,转载请留言告知!!!

详情介绍请见我的博客:

prim算法

一般来说,prim算法常用于网比较稠密的图。
对于一个点来说,找到权重最小的连接点。之后对于这两个点来说,找到这两个点中权重最小的另外一个点(即没有经过遍历的点)。之后以此类推,直到遍历完成。
可以把prim算法理解为点优先

kruskal 算法

kauskal适用于网比较稀疏的图
先对权重由小到大排序,依次遍历权重两边的点,如果点已经被遍历过,则跳过这个权重,继续遍历。直到遍历完成。
可以把kruskal算法理解为边优先

  1. 对于是否遍历过 ,我在图的博客中已经说过,可以建立一个数组,用1或0来表示是否遍历成功;
  2. 对于邻接链表来说,可以把权重放到链表里面;

下面我将通过POJ的题来对这两个算法进行解析

POJ2485

  • prim 算法
//POJ 2485高速公路 
//这里用的是邻接矩阵下的图的prime算法,即基于贪心思想的深搜遍历     
#include<stdio.h>
#define M 502
int main(void)
{
	long flag=-1,min=65537,arcs[M][M];
	int i,j,n,P=0,num,visited[M],PtT[M];//最后一个用空间换时间 
	scanf("%d",&n);
	for(int k=0;k<n;k++){
		flag=-1;P=0;		//注意第二组数据时的初始化 
		scanf("%d",&num);
		for(i=0;i<num;i++){
			for(j=0;j<num;j++)
				scanf("%d",&(arcs[i][j]));		//标准输入 
			visited[i]=0;						//初始化 
		}//for
		i=0;
		while(P<num-1){
			if(visited[i]==0){
				visited[i]=1;
				PtT[P++]=i;
				for(int temp=0;temp<P;temp++)	//PtT用来记录i 
					for(j=0;j<num;j++)
						if(min>arcs[PtT[temp]][j]&&arcs[PtT[temp]][j]!=0&&visited[j]==0){		//已经遍历过的所有树的子树中权重最小且没有被遍历过的 
							min=arcs[PtT[temp]][j];
							i=j;		//之前是把这个等式放到外面,就不确定j是否为0
						}
				flag<min?flag=min:flag=flag; 
				min=65537;
			}//if
		}//while
		printf("%d\n",flag);  //注意加回车 
	}//for
	return 0;
}

  • kruskal算法
//POJ 2458 高速公路
//用邻接矩阵的kruskal算法 并查集
//头文件和排序函数请自己补充
#define MAX 502
struct LINE{
	int weight;//弧的权重
	int mmp,mvp;//弧的两个节点
}node[MAX];
int coun=0,pre[MAX],sun=0;
int Find(int n) {
	while(n!=pre[n])
		n=pre[n];
	return n;
}//寻找祖宗节点
void Merge_set(LINE node) {
	int i=Find(node.mmp);
	int j=Find(node.mvp);
	if(i!=j) {
		pre[i]=j;
		coun++;	//记录路数
		sun+=node.weight;
	}
}//这条弧入集
int main() {
	int n,num;
	int temp=0;
	scanf("%d",&n);
	for(int k=0; k<n; k++) {
		scanf("%d",&num);
		for(int i=1; i<num*num/2; i++)
			pre[i]=i;//此时并查集都是独立的
		for(int i=1; i<=num; i++)
			for(int j=1; j<=i; j++) {	//倒三角输入
				scanf("%d",&node[temp].weight);
				node[temp].mmp=i;
				node[temp].mvp=j;
				temp++;
			}
		sort(k,k+num*num/2,comp);
		for(int i=0; i<num*num; i++)
			Merge_set(node[i]);
		coun=0;
		sun=0;
		printf("%d",sun);
	}
	return 0;
}

这里讲到了并查集的概念,那么

何谓并查集?

并查集是一个包含特定数值的集合。我们不能像prime算法那样用一个visit数组来标记是否遍历,因为prim是点优先而kruskal是边优先。看下图:
krusakl
如上所示,第一步是链接A和B,第二步是链接D和C,如果按照此时把每个点都标记的话,很显然,在B和C没连到一块的时候4个点就已经标记完成了。
故此时需要 知道他们这些点是不是已经连到一块了。很显然,如果每次用深搜或者广搜来检查的话有很麻烦,此时并查集就应运而生。
判断是否在一个并查集的标准就是祖宗是否一样。祖宗可以理解为几个相连点之间的任意一个点,为了容易判断,就会把祖宗设为一条线中开始的那个点,譬如上图,就可以设A为祖宗。请想一想,如果每个点有相同的祖宗,不就证明了他们在同一条线上,也就是说在同一个并查集中。
刚开始时,每个点都是一个不同的并查集,因为他们互不相连。每加入一个点的时候,就要通过find函数找出它的祖宗,如果祖宗不同,就把他们的祖宗设为一个,即归到一个并查集当中去。

int Find(int n) {
	while(n!=pre[n])
		n=pre[n];
	return n;
}//寻找祖宗节点
void Merge_set(LINE node) {
	int i=Find(node.mmp);
	int j=Find(node.mvp);
	if(i!=j) {
		pre[i]=j;
		coun++;	//记录路数
		sun+=node.weight;
	}
}//这条弧入集

那么,
就到这里了!

### 回答1: Prim算法Kruskal算法都是最小生成树算法。 Prim算法中的一个顶点开始,每次找到已经找到的顶点集最近的顶点,直到所有顶点都在已经找到的顶点集中。 Kruskal算法按照边的权值从小到大依次加入边,并且保证不会形成环。 Prim算法适用于稠密Kruskal算法适用于稀疏。 总的来说,Prim算法Kruskal算法都可以用来求最小生成树,但它们的适用情况不同。 ### 回答2: Prime算法Kruskal算法都是解决最小生成树问题的算法,下面我们将它们进行比较。 首先,Prime算法属于单源最短路径算法,而Kruskal算法是一种贪心算法Prime算法从一个初始节点出发,逐步扩展生成最小生成树,而Kruskal算法则是根据边的权值递增的顺序来选取边,直到生成最小生成树。 其次,Prime算法基于节点进行操作,每次选择最短的边加入生成树,然后再选择与生成树相连的最短边,直到生成树涵盖所有节点。而Kruskal算法则是基于边进行操作,每次选择权值最小且不会形成环路的边加入生成树。 此外,Prime算法的时间复杂度为O(V^2),其中V是节点数,因为每次都要选择最短的边。而Kruskal算法的时间复杂度为O(ElogE),其中E是边数,因为每次都要对边进行排序。 另外,Prime算法的实现较为简单,只需要维护一个优先队列或者使用邻接矩阵进行计算。而Kruskal算法需要使用并查集来判断是否形成环路,并且要对所有边进行排序操作。 总结来说,Prime算法Kruskal算法在解决最小生成树问题上都有各自的优点使用场景。Prime算法适用于稠密,而Kruskal算法适用于稀疏。根据具体的情况选择合适的算法能够更有效地解决问题。 ### 回答3: Prime算法Kruskal算法是解决最小生成树问题的两种常见算法。 首先,Prime算法是一种贪心算法。该算法从一个起始顶点开始,依次选择与当前生成树集合连接的代价最小的边,直到生成一棵包含所有顶点的最小生成树Prime算法的优点是简洁高效,时间复杂度为O(V^2),其中V表示顶点数。与Kruskal算法相比,Prime算法更适合处理稠密,因为它的时间复杂度与边数无关。然而,Prime算法在处理稀疏时效果不佳。 而Kruskal算法是一种基于边的贪心算法。该算法首先将所有边按权重进行排序,然后依次选择权重最小的边,如果该边不会导致生成树形成回路,则将其加入生成树集合中。当生成树的边数等于顶点数减一时,停止选择。Kruskal算法的时间复杂度为O(ElogE),其中E表示边数。与Prime算法相比,Kruskal算法更适合处理稀疏,因为排序边的时间复杂度与边数成正比。然而,Kruskal算法在处理稠密时效果不如Prime算法。 总体而言,Prime算法Kruskal算法在解决最小生成树问题上有各自的优势。Prime算法适用于稠密,相对而言更高效;而Kruskal算法适用于稀疏,排序边的操作相对而言更快速。根据具体问题的特点,我们可以选择适合的算法来求解最小生成树问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值