1. 程式人生 > >演算法之最小生成樹

演算法之最小生成樹

1. 問題描述:利用貪心演算法設計策略構造一個無向連通帶權圖的最小生成樹。

最小生成樹:設G=VE)是無向連通帶權圖,即一個網路。E中每條邊(vw)的權為c[v][w]。包含G所有頂點的樹且該生成樹各邊權的總和最小(即耗費最小),則稱該生成樹為G的最小生成樹。

G=VE)是無向連通帶權圖,頂點集V=12,…,n}。

1. Prim演算法基本思想:首先置頂點集S=1},然後只要SV的真子集,就做如下貪心選擇:

選取滿足條件c[i][j]最小的邊,並將頂點j新增到S中。直到S==V時為止。在這個過程中選擇的邊就構成G的一顆最小生成樹。

2. Kruskal

演算法基本思想:首先將Gn個頂點看成n個孤立的連通分支,將所有的邊按照權值從小大大排序。然後從第一條邊開始,依權值遞增的順序處理每一條邊:

當處理到第k條邊(vw)時,如果端點vw分別是當前兩個不同連通分支T1T2中的頂點時,就用邊(vw)將T1T2連線成一個連通分支,然後繼續處理下一條邊;如果vw位於同一個連通分支中,就直接處理下一條邊;直到只剩下一個連通分支為止。

#include <iostream>
using namespace std;
#define N 6
#define MAX 10000

//頂點個數n,各邊權值c
void Prim(int n,int c[N][N])
{
    int lowcost[n], //lowcost[i]=c[i][closest[i]]即i和鄰接頂點closest[i]邊的權值
        closest[n]; //closest[i]表示頂點i的鄰接頂點(權值最小的鄰接頂點)
    bool s[n];      //頂點集s
    s[0]=true;      //將第一個頂點新增進s中
    for(int i=1;i<n;i++)  //初始化各項
    {
        lowcost[i]=c[0][i];
        closest[i]=0;
        s[i]=false;
    }
    for(int i=1;i<n;i++)
    {
        int minc=MAX;
        int j=1;

        for(int k=1;k<n;k++)    //找到lowcost陣列中最小的權值,記錄下標將該頂點新增進s中
        {
            if(lowcost[k]<minc&&s[k]==false&&lowcost[k]>0)
            {
                minc=lowcost[k];
                j=k;
            }
        }
        cout<<j+1<<" "<<closest[j]+1<<endl;//輸出選擇的邊
        s[j]=true;

        for(int k=1;k<n;k++)    //更新lowcost,看與當前j的值鄰接權值是否比與之前的j鄰接權值小,若小則更新鄰接頂點為當前的j
        {
            if(s[k]==false&&c[j][k]<lowcost[k])
            {
                //cout<<j<<","<<k<<"--"<<c[j][k]<<"--"<<lowcost[k]<<endl;
                lowcost[k]=c[j][k];
                closest[k]=j;
            }
        }
    }
}


int main()
{
    int c[N][N]={    //0表示與自身的權值,MAX表示不可到達(非鄰接頂點)
        {0,6,1,5,MAX,MAX},
        {6,0,5,MAX,3,MAX},
        {1,5,0,5,6,4},
        {5,MAX,5,0,MAX,2},
        {MAX,3,6,MAX,0,6},
        {MAX,MAX,4,2,6,0}
        };
    Prim(N,c);
}

複雜度分析:O(n^2)