天天看點

最小生成樹

1. 無向圖與開放樹

  在講最小生成樹之前,先來說一說開放樹。連通而無環路的無向圖稱做

開放樹

。如果指定開放樹中某一頂點為根,并且把每一條邊看成是背離根的,則一棵開放樹變成一棵樹。

開放樹有2個性質:

  • 具有n>=1個頂點的開放樹包含n-1條邊
  • 如果在開放樹中任意增加一條邊,将構成一個環路

如下列兩個圖:

最小生成樹

2. 最小生成樹

隻針對無向圖

  假設E中的每條邊都有權重,為c(u,v),也叫做邊長。圖G的一棵生成樹是連接配接V中所有頂點的一棵開放樹。将生成樹中所有邊長的總和稱為生成樹的價。

使這個價最小的生成樹稱為圖的最小生成樹

  設G=(V,E)是一個連通圖,在E上定義一個權函數C,且{(V1,T1),(V2,T2),...,(Vk,Tk)}是G的任意生成森林,令

    

最小生成樹

  其中,|T| <= |E|,又假設e=(v,w)是E-T中的一條邊,其權值C[v][w]最小,而且v在V1中,w不在V1中,則圖G有一棵包含T和e的生成樹,其價不大于包含T的任何生成樹的價。

以上的性質下,兩種算法應運而生:Prim算法和Kruskal算法
  • Prim算法
/*輸入為權重無向圖G=(V,E),其中V={1,2,3,...,n}
     要點:引進集合U和T,U準備放頂點,T準備放邊。初值為U={1},T為空,選擇最小權的邊(u,v),使u在U中,v在V-U中,将v加入U,(u,v)加入T。重複這個過程 */
     
     
     void Prim(costtype C[n+1][n+1])
     {
        /*CLOSECT表示U中的頂點,(i,CLOSECT[i])具有最小的權,而LOWCOST[i]表示該邊的權,其中i不在U中  */
        
        costtype LOWCOST[n+1];
        int CLOSEST[n+1];
        int i,j,k;
        costtype min;
        for(i=2;i <= n;i++)
        {
            //初始化
            LOWCOST[i] = C[1][i];
            CLOSEST[i] = 1;
        }
        
        for(i=2;i=n;i++)
        {
            min = LOWCOST[i];
            k=i;
            //找出最小權的邊
            for(j=2;j<=n;j++) {
                if(LOWCOST[j] < min) {
                    min = LOWCOST[j];
                    k = j;
                }
            cout<<"("<<k<<","<<CLOSECT[k] << ")"<<endl;
            LOWCOST[k] = infinity;  /* k加入U */
            
            
            //對新加入的k的基礎上,更新LOWCOST和CLOSECT
            for(j = 2;j<=n;j++) {
                if(C[k][j] < LOWCOST[j] && LOWCOST[j] != infinity) {
                    LOWCOST[j] = C[k][j];
                    CLOSECT[j] = k;
                }
            }
        }
     }
           
  • Kruskal算法
void Kruskal(V,T)
    {
        T = V;
        ncomp = n;
        while(ncomp > 1) {
            從E中取出并删除權最小的邊(v,u)
            if(v和u屬于T中不同的連通分量) {
                T= T + (v,u);
                ncomp --;
            }
        }
    }
           
Prim算法複雜度為O(n2),而Kruskal算法複雜度為O(ne),故Prim算法适用于點較少的情況

3. 最小樹形圖

隻針對有向圖

  首先為除根之外的每個點標明一條入邊,這條入邊一定要是所有入邊中最小的。現在所有的最小入邊都選擇出來了,如果這個入邊集不存在有向環的話,我們可以證明這個集合就是該圖的最小樹形圖。這個證明并不是很難。如果存在有向環的話,我們就要将這個有向環縮成一個人工頂點,同時改變圖中邊的權。假設某點u在該環上,并設這個環中指向u的邊權是in[u],那麼對于每條從u出發的邊(u, i, w),w為權,在新圖中連接配接(new, i, w)的邊,其中new為新加的人工頂點; 對于每條進入u的邊(i, u, w),在新圖中建立邊(i, new, w-in[u])的邊。

最小生成樹
int ZLEdmonds(int n,int map[maxn][maxn])  
    {  
        bool visited[maxn],flag[[maxn];  
        int pre[maxn];  
        int sum=0,i,j,k;  
        for( i=0; i<n; i++){  
             flag[i]=false;  
             map[i][i]=INF;  
        }  
        pre[0]=0;  
        while( true){  
               //求最短弧集合E0。  
               for( i=1; i<n; i++){  
                    if( flag[i]) continue;  
                    pre[i]=i;  
                    for( j=0; j<n; j++){   //pre[i]儲存終點為i的最短弧的起點。  
                         if( !flag[j]&&map[j][i]<map[pre[i]][i])  
                             pre[i]=j;      
                    }  
                    if( pre[i]==i) return -1;  
               }   
               //檢查E0  
               for( i=1; i<n; i++){  
                    if( flag[i]) continue;  
                    for( j=0; j<n; j++)  
                         visited[j]=false;  
                    visited[0]=true;  
                      
                    j=i;  
                    do{  
                        visited[j]=true;  
                        j=pre[j];  
                    }while( !visited[j]);   
                    if( !j) continue; //沒有找到環。  
                       
                    i=j;//将整個環的權值儲存,累計入原圖的最小樹形圖  
                    do{  
                        sum+=map[pre[j]][j];  
                        j=pre[j];  
                    }while( j!=i);  
                      
                    j=i;//對于環上的點有關的邊,修改邊權  
                    do{  
                        for( k=0; k<n; k++){  
                             if( !flag[k]&&map[k][j]&&map[k][j]<INF&&k!=pre[j])  
                                 map[k][j]-=map[pre[j]][j];  
                        }  
                        j=pre[j];  
                    }while( j!=i);  
                      
                    //縮點,将整個環縮成i号點,所有環上的點有關的邊轉移到點i  
                    for( j=0; j<n; j++){  
                         if( j==i) continue;  
                         for( k=pre[i]; k!=i; k++){  
                                
                              if( map[k][j]<map[i][j])  
                                  map[i][j]=map[k][j];  
                              if( map[j][k]<map[j][i])  
                                  map[j][i]=map[j][k];   
                         }  
                    }  
                    //标記環上其他的點為被縮掉  下次再找Ei時不參與   
                    for( j=pre[i]; j!=i; j=pre[j])  
                         falg[j]=true;  
                    //目前環縮點結束,形成新的圖G',跳出繼續求G'的最小樹形圖 ,累計入sum。   
               }   
               if( i==n){  
                   for( i=0; i<n; i++)  
                        if( !flag[i])  
                            sum+=map[pre[i]][i];  
                   break;  
               }  
        }          
             return sum;          
    }  
           

作者: vachester

出處:http://www.cnblogs.com/vachester/

郵箱:[email protected]

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利。

上一篇: 圖的搜尋