1. 程式人生 > >prime演算法詳解【最小生成樹】

prime演算法詳解【最小生成樹】

無以言表我對著程式碼懵了兩個小時終於看懂了的雞凍,手寫程式大法好哇,【或者只是我太久沒敲程式碼了。。】個人感覺這個演算法還是有點粗魯,大量的遍歷,比較中意最小生成樹的另一個演算法,一會搞懂它的程式碼再說。

最小生成樹:

    圖G是當且僅當以下任意條件成立:

           1》G有|V|-1條邊,無環;【v為圖中節點數目】

           2》G有|V|-1條邊,連通;

           3》任意兩點之間只有唯一的簡單路徑;

           4》Gl連通,但刪除任意一條邊之後就不連通。

    最小生成樹

            在一個帶權的【帶權的:連線各點的邊上有值,通常表示距離,花費等等】無相連通圖中,各邊權和【即各邊價值的和】最小的一顆生成樹即為原圖的最小生成樹。

prime演算法求最小生成樹:

      【如果不瞭解,圖,生成樹這類的概念的不建議直接看演算法,雖然看明白之後就是一個二階矩陣,呃,,反正我是把之前的補了之後才看明白的。】

     演算法思想:

            prim演算法是一種貪心演算法,其從原圖A"A"代表一個圖!】任意挑選一個點point,【想想為啥麼任意,最小生成樹無論挑選哪條邊,所有的節點必須與原圖相同,一個不能落下;再聯絡樹的連通性:從任意一點出發,可到達任意一點;是第一個都無所謂,生成好的最小樹換一種提溜方式,所謂的起點也隨之變化了】,將挑選的這一點point視為圖B的起點【圖B就是未來的天之驕子,最小生成樹】。

            然後在原圖A中找,找與起點point相連的各個邊,每一條邊,瞅瞅誰最小,把這條最小的邊以及這個邊對應著的點p2,一併加入到圖B中。現在有了小小收穫,看看B中此時有什麼,起點point與一條它能連到的最小的一條邊,以及該邊的另一端p2,通通收入囊中了。

            我們的目標是將所有的點都搞到B中去,好歹現在多了一個幫手,再找誰?從p2開始找嗎?還是挑選起點point的次小邊?看看我們演算法的本質,貪心!當然兩者都要,【這裡演算法實現略微變態,有點難懂,記住這裡的思想過程】,我們要將魔爪伸向剩餘的點了,我們一開始比較的時候將起點point能連線的邊都拽了出來,每一個邊背後都帶著一個無辜點,當然也有點不與起點point相連

逃過一劫,但是現在起點point多了一個幫手p2,不與起點相連但與p2相連的點自然也被拽了出來,這時候的問題是,與起點和p2都相連的點,到底留下哪一條邊呢?當然憑實力說話,誰的權值更小留哪一條邊。

            先在將所有被起點與p2點一起拽出來的邊再進行比較,留下最小的那一條邊以及該邊拽出來的那個點,然後就是這三個點一起作威作福,拽剩下的幾個點了,處理過程同上,,直到所有的點都從A中被拽過來。

【說的有點複雜,一如我死活看不懂程式碼最後一步一步寫時內心的瘋狂diss】,簡單來說,就是不斷從圖A中挑選與圖B中點相連的最短的邊以及對應點加入圖B,直至所有點加入圖B。

     演算法步驟:

            選用圖中的任意一個頂點V0,從V0開始生成最小生成樹:

            1》初始化dist[v0]=0,其他點的距離值dist[i]=正無窮;其中dist [ i ] 表示集合VB中的點到VA中的點的距離值,

            2》經過N次如下步驟操作,最後得到一個含N個頂點,N-1條邊的最小生成樹:

                   1>選擇一個未標記的點K,並且dist [ k ] 的值是最小的;

                   2>標記點K進入集合VA;

                   3>以K為中間點,修改未標記點 j ,即VB中的點到VA的距離值;

            3》得到最有生成樹T。

     程式碼實現:

//最小生成樹
//prim演算法

#include<iostream>
#include<cstring>
#define INF 10000
using namespace std;
const int N = 6;
bool visit[N];
int dist[N] = { 0, };
int G[N][N] = { {INF,7,4,INF,INF,INF},  //INF代表兩點之間不可達
                    {7,INF,6,2,INF,4},
                    {4,6,INF,INF,9,8},
                    {INF,2,INF,INF,INF,7},
                    {INF,INF,9,INF,INF,1},
                    {INF,4,8,7,1,INF}
                  };
int prim(int cur)//首個挑選到的節點 cur
{
    int index = cur;
    int sum = 0;       //最短路徑之和
    int i = 0 , j = 0;
    cout << index << " ";
    memset(visit,false, sizeof(visit));  //標記陣列,初始化,全部未訪問
    visit[cur] = true;
    for(i = 0; i < N; i++)//與第一個挑走的點i,相連的所有邊的距離,存入dist[]中
        dist[i] = G[cur][i];//初始化,每個與a鄰接的點的距離存入dist

    for(i = 1; i < N; i++)//遍歷表中每一個節點
    {
        int minn= INF;   //與另一個表相連的最小邊,初始化,為一個極大值
        for(j = 0; j < N; j++)
        {
            if(!visit[j] && dist[j] < minn)          //找到未訪問的點中,距離當前最小生成樹距離最小的點
            {
                minn = dist[j];  //不斷更新與點cur的相連點的最短距離
                index = j;
            }
        }
        visit[index] = true;//標記距離最短的剛剛被拿走的那個節點,已經被訪問過
        cout << index << " ";
        sum += minn;
        for(j = 0; j < N; j++)  //已經進入最小樹的點的可及範圍,與剛進入最小樹的點相比較,比之前點可到達某點的距離較小,更新當前最小生成樹的可及範圍的最小值*****
        {
            if(!visit[j] && dist[j]>G[index][j])      //執行更新,如果點距離當前點的距離更近,就更新dist
            {
                dist[j] = G[index][j];
            }
        }
    }
    cout << endl;
    return sum;               //返回最小生成樹的總路徑值
}
int main()
{
    cout << prim(0) << endl;//從頂點a開始
    return 0;
}


程式碼自己扣吧,難點在與dist[ ]的更新操作上,如果看不明白,,,看註釋。