1. 程式人生 > 其它 >No.8.2 圖的最小生成樹(2)

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;
}