对于最小生成树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;
}