最小生成树之prim算法&&kurskal算法

首先,我们来说一下图当中的最小生成树,对于一个无向图来说,它的最小生成树的定义如下:
1,该数经过所有的结点
2,经过的所有的结点的边权之和最小
3,这棵树的起点可以是任意的点,不过一般都是题目中指定的
prim算法
适用范围:边多点少的稠密图。
复杂度:O(v^2)
与Dijkstra算法相比,该算法的区别是里面的d数组,Dijkstra算法的d表示的是当前的结点v距离起始节点s的最短距离,而该算法的d表示的是当前结点和它的上一个结点之间的最短距离。我们的Dijkstra算法里面的起点在我们的prim算法中是一个集合S,prim算法的思路是对边进行一个贪心,逐个逐个边进行贪心,最后得到最优解。
代码:

const int maxn=2020;
const int INF=1000000000;

int G[maxn][maxn],d[maxn];
bool vis[maxn]={false};

int prim(int s){
	fill(d,d+maxn,INF);
	d[s]=0;
	int ans=0;
	for(int i=0;i<ans;i++){
		int u=-1,MIN=INF;
		for(int j=0;j<n;j++){
			if(vis[j]==false&&d[j]<MIN){
				MIN=d[j];
				u=j;
			}
		}
		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]<d[v]&&G[u][v]!=INF){
				d[v]=G[u][v];   //更新路径长度
			}
		}
	}
	return ans;
}

kurskal算法
适用范围:点多边少的稀疏图
复杂度:O(ElogE),E为图的边数
该算法主要是通过从整体进行一个贪心的策略,检查所有的边,找出权最小的边进行存储。
但是,还有两个前提:
1,如果加入这条边之后与已经加入的边形成一个连通图,也就是会形成环,那么就不能把这条边加入。
2,如果此时的边的数量等于结点数量-1的话,那么就可以直接退出循环了。
主要需要解决的是两个问题:
1,怎么确定加上这条边后会不会是一个连通图?这个可以用之前学过的并查集先进行处理,然后后面要用到的时候再进行判断。
2,如何保存我们的加入的边?还是用到并查集,通过并查集中的合并的函数来进行处理。
代码:

const int maxn=100;

struct edge{
	int u,v;
	int cost;
}E[maxn];
bool cmp(edge a,edge b){
	return a.cost<b.cost;
}
int father[maxn];
int n,m;

int findfather(int x){  //并查集和路径压缩 
	int a=x;
	whiel(x!=father[x]){
		x=father[x];
	}
	while(a!=father[a]){
		int z=a;
		a=father[a];
		father[z]=x;
	}
	return x;
}

int kruskal(int n,int m){
	int ans=0,Num_edge=0;
	for(int i=0;i<n;i++){   //初始化 
		father[i]=i;
	}
	sort(E,E+m,cmp);
	for(int i=0;i<m;i++){
		int fau=findfather([E[i].u]);
		int fav=findfather([E[i].v]);
		if(fau!=fav){  //如果边的两边的点不是连通块 
			father[fau]=fav;
			ans+=E[i].cost;
			Num_edge++;
			if(Num_edge==n-1)    break;
		}
	}
	if(Num_edge!=n-1)  return -1;
	else  return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值