1. 程式人生 > >最小生成樹-Prim算法與Kruskal算法

最小生成樹-Prim算法與Kruskal算法

概念 ret using 2.3 邊集數組 數組存儲 ons graph 之間

一、最小生成樹(MST)

  ①、生成樹的代價:設G=(V,E)是一個無向連通網,生成樹上各邊的權值之和稱為該生成樹的代價。

  ②、最小生成樹:在圖G所有生成樹中,代價最小的生成樹稱為最小生成樹。

  最小生成樹的概念可以應用到許多實際問題中。 例:在n個城市之間建造通信網絡,至少要架設n-1條通信線路,而每兩個城市之間架設通信線路的造價是不一樣的,那麽如何設計才能使得總造價最小?

  ③、MST性質:假設G=(V, E)是一個無向連通網,U是頂點集V的一個非空子集。若(u, v)是一條具有最小權值的邊,其中u∈U,v∈V-U,則必存在一棵包含邊(u, v)的最小生成樹。

二、Prim算法

  ①基本思想:設G=(V, E)是具有n個頂點的連通網,T=(U, TE)是G的最小生成樹, T的初始狀態為U={u0}(u0∈V),TE={ },重復執行下述操作:在所有u∈U,v∈V-U的邊中找一條代價最小的邊(u, v)並入集合TE,同時v並入U,直至U=V。

  關鍵:是如何找到連接U和V-U的最短邊,利用MST性質,可以用下述方法構造候選最短邊集:對應V-U中的每個頂點,保留從該頂點到U中的各頂點的最短邊。

  ②數據結構設計

  數組lowcost[n]:用來保存集合V-U中各頂點與集合U中頂點最短邊的權值,lowcost[v]=0表示頂點v已加入最小生成樹中;

  數組adjvex[n]:用來保存依附於該邊(集合V-U中各頂點與集合U中頂點的最短邊)在集合U中的頂點。

  如何用數組lowcost和adjvex表示候選最短邊集?

  lowcost[i]=w;表示頂點vi和頂點vk之間的權值為w,

  adjvex[i]=k;其中:vi∈ V-U 且vk ∈U。

  ③Prim算法——偽代碼

1. 初始化兩個輔助數組lowcost和adjvex;
2. 輸出頂點u0,將頂點u0加入集合U中;
3. 重復執行下列操作n-1次
   3.1 在lowcost中選取最短邊,取adjvex中對應的頂點序號k;
   3.2 輸出頂點k和對應的權值;
   3.3 將頂點k加入集合U中;
   3.4 調整數組lowcost和adjvex;

  ④C++實現

 1
#include<iostream> 2 #include<fstream> 3 using namespace std; 4 5 #define MAX 100 6 #define MAXCOST 0x7fffffff 7 8 int graph[MAX][MAX]; 9 10 /*方法二*/ 11 struct Node 12 { 13 int lowcost;// 權值 14 int adjvex;// 候選最短邊的鄰接點 15 }; 16 Node shortEdge[MAX];// 候選最短邊集合 17 void prim2(int graph[][MAX], int n) 18 { 19 // 初始化輔助數組shortEdge,所有點到點1的權值 20 for (int i = 2; i <= n; i++) 21 { 22 shortEdge[i].lowcost = graph[1][i]; 23 shortEdge[i].adjvex = 1; 24 } 25 shortEdge[1].lowcost = 0;// 將頂點1加入集合U 26 for (int i = 2; i <= n; i++) 27 { 28 // 尋找最短邊的鄰接點k 29 int k = 0, min = MAXCOST; 30 for (int j = 2; j <= n; j++) 31 { 32 if (min >shortEdge[j].lowcost&&shortEdge[j].lowcost!=0) 33 { 34 min = shortEdge[j].lowcost; 35 k = j; 36 } 37 } 38 cout << "(" << k << shortEdge[k].adjvex << ")" << shortEdge[k].lowcost; 39 shortEdge[k].lowcost = 0;// 將頂點k加入結合U中 40 for (int j = 2; j <= n; j++) 41 { 42 if (graph[k][j] < shortEdge[j].lowcost) 43 { 44 shortEdge[j].lowcost = graph[k][j]; 45 shortEdge[j].adjvex = k; 46 } 47 } 48 } 49 } 50 51 int main() 52 { 53 int i, j, k, m, n; 54 int x, y, cost; 55 ifstream in("input.txt"); 56 in >> m >> n;//m=頂點的個數,n=邊的個數 57 //初始化圖G 58 for (i = 1; i <= m; i++) 59 { 60 for (j = 1; j <= m; j++) 61 { 62 graph[i][j] = MAXCOST; 63 } 64 } 65 //構建圖G 66 for (k = 1; k <= n; k++) 67 { 68 in >> i >> j >> cost; 69 graph[i][j] = cost; 70 graph[j][i] = cost; 71 } 72 prim2(graph, m); 73 system("pause"); 74 return 0; 75 }

三、Kruskal算法

  ①基本思想:設無向連通網為G=(V, E),令G的最小生成樹為T=(U, TE),其初態為U=V,TE={ },然後,按照邊的權值由小到大的順序,考察G的邊集E中的各條邊。若被考察的邊的兩個頂點屬於T的兩個不同的連通分量,則將此邊作為最小生成樹的邊加入到T中,同時把兩個連通分量連接為一個連通分量;若被考察邊的兩個頂點屬於同一個連通分量,則舍去此邊,以免造成回路,如此下去,當T中的連通分量個數為1時,此連通分量便為G的一棵最小生成樹。

  ②數據結構設計:因為Kruskal算法是依次對圖中的邊進行操作,因此考慮用邊集數組存儲圖中的邊,為了提高查找最短邊的速度,可以先對邊集數組按邊上的權值排序。

 1 struct EdgeType
 2 {
 3     int from, to;// 邊依附的兩個頂點
 4     int weight;// 邊的權值
 5 };
 6 
 7 template<class T>
 8 struct EdgeGraph
 9 {
10     T vertex[Maxvertex];// 存放頂點信息    
11     vector<EdgeType> edge;// 存放邊的數組(用vector便於排序)
12     int vertexNum, edgeNum;//頂點數和邊數
13 };

  ③Kruskal算法——偽代碼

1. 初始化:U=V;  TE={ }; 
2. 循環直到T中的連通分量個數為1  
     2.1 在E中尋找最短邊(u,v);
     2.2 如果頂點u、v位於T的兩個不同連通分量,則
           2.2.1 將邊(u,v)並入TE;
           2.2.2 將這兩個連通分量合為一個;
     2.3 在E中標記邊(u,v),使得(u,v)不參加後續最短邊的選取;

  ④C++實現

#include<iostream>
#include<fstream>
#include<vector>
#include<algorithm>
using  namespace std;

const int Maxvertex = 10;// 最多頂點數
const int MaxEdge = 100;// 最多邊數

struct EdgeType
{
    int from, to;// 邊依附的兩個頂點
    int weight;// 邊的權值
};

template<class T>
struct EdgeGraph
{
    T vertex[Maxvertex];// 存放頂點信息    
    vector<EdgeType> edge;// 存放邊的數組(用vector便於排序)
    int vertexNum, edgeNum;//頂點數和邊數
};
int FindRoot(int parent[], int v)// 求頂點的雙親結點
{
    int t = v;
    while(parent[t]> -1)
        t = parent[t];    
    return t;
}
void Kruskal(EdgeGraph<int> G)
{
    int parent[Maxvertex];
    for (int i = 0; i < G.vertexNum; i++)
        parent[i] = -1;// 表示頂點i沒有雙親結點
    for (int num = 0, i = 0; i < G.edgeNum; i++)
    {
        int vex1 = FindRoot(parent, G.edge[i].from);
        int vex2 = FindRoot(parent, G.edge[i].to);
        if (vex1 != vex2)
        {
            cout << "(" << G.edge[i].from << G.edge[i].to << ")" << endl;
            parent[vex2] = vex1;// 合並生成樹
            num++;
            if (num == G.vertexNum - 1)
                return;
        }
    }

}

bool sort_by_weight(EdgeType&k1, EdgeType&k2)
{
    return k1.weight < k2.weight;
}
int main()
{
    EdgeGraph<int> Edgraph;// 存放圖的信息
    ifstream in("input.txt");
    in >> Edgraph.vertexNum >> Edgraph.edgeNum;
    //構建圖G
    for (int k = 0; k <Edgraph.edgeNum; k++)
    {
        EdgeType temp;
        in >> temp.from >> temp.to >> temp.weight;
        Edgraph.edge.push_back(temp);
    }
    // 將邊按照權值排序
    sort(Edgraph.edge.begin(), Edgraph.edge.end(), sort_by_weight);
    Kruskal(Edgraph);
    system("pause");
    return 0;
}

input.txt
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6

最小生成樹-Prim算法與Kruskal算法