图的最小生成树Prim堆优化

图的最小生成树Prim堆优化适用于稀疏图
复杂度为O(MlogN)

例:
在这里插入图片描述
输入

6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2

选出最少的边让图连通,使得边的总长度之和最短。

算法的流程

1.以邻接表的方式存储无向图,从任意一个顶点开始构造生成树,假设从1号顶点开始,用book记录哪些顶点已被选中。

2.用h数组表示堆,pos数组存编号在堆中的位置,dis数组存储编号在对应的值(生成树到各个顶点的最小距离),没直连边时设为dis无穷大,dis初始化编号1号开始dis[1]=0,然后搜索与1点直接相连的顶点对应的边更新dis值,然后把所有编号在堆中随便放,再建立最小堆,最后将编号1弹出(dis[1]为0)。

3.弹出堆的顶点(即数组dis中离生成树最近的点)假设这个点为x,加入到生成树中,更新x到每个直连边的距离(松弛)即:
dis[v[i]] =min(dis[v[i]],w[i])
最后再维护堆。

4.重复第3步直到生成树中有n个顶点为止

代码实现如下:

#include<stdio.h>
#define INF 99999999

int n,m,i,j,sum=0,count=0,size;//size表示堆的大小 
//h[i]表示堆的i位置对应的编号,pos[i]表示i编号在堆中的位置 
int first[50],next[100],u[100],v[100],w[100],book[50]={0},dis[50],h[50],pos[50]; 
//交换 
void swap(int a,int b){
	//交换堆的位置 
	int t=h[a];
	h[a]=h[b];
	h[b]=t;
	//更新记录 
	t=pos[h[a]];
	pos[h[a]]=pos[h[b]];
	pos[h[b]]=t;
	return;
}
//向下调整 
void siftdown(int o){
	int t,flag=0;
	while(o*2 <= size && flag==0){
		if(dis[h[o]] > dis[h[o*2]]){
			t=o*2;
		} else t=o;
		
		if(o*2+1 <= size){
			if(dis[h[t]] > dis[h[o*2+1]])
			t=o*2+1;
		}
		
		if(t!=o){
			swap(t,o);
			o=t;
		} else flag=1; 
	}
	return;
}
//向上调整 
void siftup(int k){
	int flag=0;
	
	while(flag==0 && k!=1){
		if(dis[h[k]] < dis[h[k/2]])
			swap(k,k/2);
		else flag=1;
		k=k/2;
	}
	return;
}

int pop(){
	int t=h[1];
	pos[t]=0;  //t点的位置放一边,可写可不写 
	h[1]=h[size]; //堆的第一个位置的编号变为最后一个位置的编号 
	pos[h[1]]=1; //h[1]点的位置记录为1 
	size--;
	siftdown(1);
	
	return t;
}

int main(){
	scanf("%d %d",&n,&m);
	for(i=1; i <=n; i++){
		first[i]=-1;
		dis[i]=INF;
	}
	
//邻接表存储双向图 
	for(i=1; i <= m; i++){
		scanf("%d%d%d",&u[i],&v[i],&w[i]);
		u[m+i]=v[i];
		v[m+i]=u[i];
		w[m+i]=w[i];
	}	
	for(i=1; i <= 2*m; i++){
		next[i]=first[u[i]];
		first[u[i]]=i;
	}
//Prim核心部分 
//假设以1为出发点 
	dis[1]=0;
	book[1]=1;
	count++;
	for(i=first[1]; i!=-1; i=next[i]){
		dis[v[i]]=w[i];
	}
		
	size=n;
	for(i=1; i<=size; i++){
		h[i]=i;
		pos[i]=i;
	}
	for(i=size/2; i>= 1; i--){//建最小堆 
		siftdown(i);
	}
	pop();//先把编号1弹出 
	
	while(count<n){
		j=pop();//返回编号值 
		book[j]=1;
		sum+=dis[j]; //写成dis[h[j]]debug好久QAQ 
		count++;
		//松弛 
		for(i=first[j]; i != -1; i=next[i]){
			if(book[v[i]]==0 && dis[v[i]] > w[i]){
				dis[v[i]] = w[i];
				siftup(pos[v[i]]);
			}
			
		}
	}
	printf("%d",sum);
	return 0;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值