No.8.2 圖的最小生成樹(2)
一、上節中的最小生成樹,使用了快速排序演算法;這節的最小生成樹,模擬Dijkstra演算法
1.既然是找一條路(最小生成樹),將所有節點連線起來,那麼隨便選擇一個頂點先,然後從這個頂點出發的邊中,挑一個最短的邊;然後再找一個距離這兩個頂點最近的邊和頂點,加入到圖中,同時要避免迴路;依次類推,直到加入圖的頂點數 == 頂點數。
2.是不是有點像Dijkstra演算法:先固定一個點,找到距離源點A最近的一個點B;然後固定點B,再找到距離B最近的點。。。
3.不同的地方就在於:Dijkstra演算法是尋找的各頂點到源點的最短距離 dis[k] > dis[j] + e[j][k];最小生成樹,則是尋找所有樹外頂點到任一個生成樹頂點的最短距離 dis[k] > e[j][k];
什麼意思呢?
a. 比如現在樹節點(1,2,4),非樹節點(3,5,6),新加入的樹節點是4(可以認為 dis[4]=0),掃描以4為初始點的邊4->5,並比對 dis[5] 的值,(此時Dijkstra演算法dis[k] > dis[j] + e[j][k] = e[j][k] ),說明通過此時的樹節點 4,可以使非樹節點 5 到某個樹節點的距離變短;
b. 更新 dis 陣列,再找到非樹節點中的最小值加入到樹節點;
c. 繼續以新加入的節點更新非樹節點到樹節點的距離!
二、CODE
# include <stdio.h>
int main(){
int i,j,k, t1,t2,t3;
int min,n,m;
int inf= 999999;
int book[7]={0};
int e[7][7],dis[7];
int count=0,sum=0;
scanf("%d %d", &n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=inf;
}
for(i=1;i<=m;i++){ //無向圖
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
e[t2][t1]=t3;
}
for(i=1;i<=n;i++) //先將 1 加入生成樹,並更新非樹節點到樹節點(1,)的距離
dis[i]=e[1][i];
book[1]=1;
count++;
while(count<n){
min=inf;
for(i=1;i<=n;i++){ //在非樹節點中,找出最短路徑
if(book[i]==0 && dis[i]<min){
min=dis[i];
j=i;
}
}
book[j]=1;
count++;
sum=sum+dis[j];
for(k=1;k<=n;k++){ //掃描當前頂點 j 的所有邊,並以 j 為中間點,更新生成樹到非樹節點的距離
if(book[k]==0 && dis[k] >e[j][k])
dis[k]=e[j][k];
}
}
printf("%d ",sum);
getchar();return 0;
}
顯然,這種方法的時間複雜度是O(N*N)。
三、堆優化查詢,鄰接表優化儲存
dis[ ] 記錄生成樹到樹外頂點的距離;
h[ ]是一個最小堆,以邊長為準,儲存的是頂點編號;
pos[ ]記錄每個頂點在最小堆中的位置;
emmm,沒看懂,先行記錄,留作後續!
int dis[7],book[7]={0};
int h[7],pos[7],size;
void swap(int x,int y){
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){
int t,flag=0;
while(i*2<=size && flag==0){
if(dis[h[i]]>dis[h[i*2]])
t=i*2;
else
t=i;
if(dis[h[t]] > dis[i*2+1])
t=2*i+1;
if(t!=i){
swap(t,i);
i=t;
}
else
flag=1;
}
}
void siftup(int i){
int flag=0;
if(i==1) return;
while(i!=1 && flag==0){
if(dis[h[i]]<dis[h[i/2]])
swap(i,i/2);
else
flag=1;
i=i/2;
}
}
int pop(){
int t;
t=h[1];
pos[t]=0;
h[1]=h[size];
pos[h[1]]=1;
size--;
siftdown(1);
return t;
}
int main(){
int n,m,i,j,k;
int u[19],v[19],w[19],first[7],next[19];
int inf=999999;
int count=0,sum=0;
scanf("%d %d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d %d %d",&u[i],&v[i],&w[i]);
for(i=m+1;i<=2*m;i++){
u[i]=v[i-m];
v[i]=u[i-m];
w[i]=w[i-m];
}
for(i=1;i<=n;i++) first[i]=-1;
for(i=1;i<=2*m;i++){
next[i] = first[u[i]];
first[u[i]]=i;
}
book[1]=1;
count++;
dis[1]=0;
for(i=2;i<=n;i++) dis[i]=inf;
k=first[1];
while(k!=-1){
dis[v[k]]=w[k];
k=next[k];
}
size=n;
for(i=1;i<=size;i++) {h[i]=i; pos[i]=i;}
for(i=size/2;i>=1;i--) {siftdown(i);}
pop();
while(count<n){
j=pop();
book[j]=1;
count++;
sum=sum+dis[j];
k=first[j];
while(k!=-1){
if(book[v[k]]==0 && dis[v[k]]>w[k]){
dis[v[k]]=w[k];
siftup(pos[v[k]]);
}
k=next[k];
}
}
printf("%d ",sum);
getchar();getchar();return 0;
}