最小生成树Prim算法 堆优化

对于最小生成树prim算法中,我们每次要扫描一遍邻接表才能找到最小的边的点,但是如果利用堆这种数据结构来进行优化,我们可以大大减小这种查找的时间消耗

我们利用邻接表和小根堆来进行优化,下面是代码解析

#include"iostream"
#include"cstdio"


using namespace std;


int h[100];
int u[100];
int v[100];
int w[100];
int first[100];
int next[100];
int n,m;
int size;
int book[100];
int dis[100];
int pos[100];
int sum=0;
int count=0;
int inf=999999999;


void swap(int x,int y)                          //pos数组记录的是顶点在堆中的编号
{
int t;
t=h[x];
h[x]=h[y];
h[y]=t;

t=pos[h[x]];
pos[h[x]]=pos[h[y]];
pos[h[y]]=t;
}


void siftdown(int i)                   //这里我们的下沉和上移函数的判断标准是dis数组的大小
{
int t,flag=0;
while(i*2<=size&&flag==0)
{
if(dis[h[i]]>dis[h[i*2]])
{
t=2*i;

else
{
t=i;
}
if(i*2+1<=size)
{
if(dis[h[t]]>dis[h[i*2+1]])
{
t=i*2+1;
}
}
if(t!=i)
{
swap(i,t);
i=t;
}
else
{
flag=1;
}
}
}


void siftup(int i)
{
int flag=0;
if(i==1)
{
return ;
}
else
{
while(i!=1&&flag==0)
{
if(dis[h[i]]<dis[h[i/2]])
{
swap(i,i/2);
i=i/2;
}
else
{
flag=1;
}
}
}
}


int  pop()                   //弹出堆顶的元素,返回堆顶的编号
{
int t;
t=h[1];
h[1]=h[size];
size--;
pos[h[1]]=1;
siftdown(1);
return t;
}


void prepare()                       //前期准备函数
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);
}
for(int i=m+1;i<=2*m;i++)                  //因为是无向图,我们必须要进行双向存储

{
u[i]=v[i-m];
v[i]=u[i-m];
w[i]=w[i-m];
}
for(int i=1;i<=n;i++)                         //邻接表必须要开始的时候给first全部复位NULL(-1)
{
first[i]=-1;
}
for(int i=1;i<=2*m;i++)
{
next[i]=first[u[i]];
first[u[i]]=i;
}
for(int i=1;i<=n;i++)
{
dis[i]=inf;
}
dis[1]=0;
int k=first[1];                   //给dis数组进行赋值
while(k!=-1)
{
dis[v[k]]=w[k];
k=next[k];
}
book[1]=1;
count++;

size=n;                      //开始构造堆
for(int i=1;i<=n;i++)
{
h[i]=i;
pos[i]=i;
}
for(int i=n/2;i>=1;i--)
{
siftdown(i);
}
pop();                //j将起点弹出(起点在堆顶的原因是我们在开始的时候对堆顶的dis值复位了0,为最小)
}


int main()
{
prepare();
int j,k;
while(count<n)
{
j=pop();
        book[j]=1;
count++;
sum+=dis[j];
k=first[j];
while(k!=-1)                //以j为起点对j的出边进行松弛,更新dis数组(dis记录目前生成树到未在生成树中的点的距离)
{
if(book[v[k]]==0&&dis[v[k]]>w[k])
{
dis[v[k]]=w[k];
siftup(pos[v[k]]);
}
k=next[k];
}
}
cout<<sum<<endl;
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值