最小生成樹-prim

最小生成樹

首先明白兩個概念,什麼是生成樹以及最小生成樹?
相關概念引入:

  1. 連通:如果頂點v和v’之間由路徑,則稱v和v’是連通的
  2. 連通圖:如果圖中任意兩個頂點都是連通的,則稱其爲連通圖
  3. 連通子圖:子圖中的頂點爲原圖的子集,且子圖是連通的

生成樹: 一個極小連通子圖,它包含圖中所有頂點,但是僅包含n-1條邊(n爲頂點個數)
最小生成樹: 在生成樹的基礎上,其各邊權值之和最小

如何構建生成樹?

MST性質: 假設N是一個連通網,U是頂點集V的一個非空子集。若(u, v)是一條具有最小權值的邊,其中u屬於U,v屬於V-U,則必存在一棵包含邊(u,v)的最小生成樹。
最小生成樹要求:

  • 包含圖中所有頂點
  • n個頂點僅包含n-1條邊
  • 各邊權值之和最小

先看要求三:權值之和最小,即每次選擇權值最小的邊即可。要求二:即保證圖中不存在環。要求一:每個頂點都要被選中。下面基於prim算法介紹如何構建最小生成樹。

prim算法

構造過程:
  1. 首先選擇一個開始頂點(因爲最小生成樹要求是包含所有頂點,因此選擇哪個開始頂點並不會影響最終結果)
  2. 將選中頂點放入集合U中,選擇集合U中的頂點到V-U中的最短路徑。
  3. 將選中的頂點(V-U內的頂點)放入集合U
  4. 重複步驟2、3,直至U = V
    在這裏插入圖片描述

如上圖所示:首選頂點V1,將V1加入集合U。選擇V1到V-U內頂點距離最短的頂點V3,將V3加入集合U。選擇U內頂點到V-U內頂點距離最近的頂點,這裏需要注意的是:是從全局的角度選擇,不是從單一頂點選擇最近的頂點。例如上圖,選擇V4結束,沒有可選的後,並沒有回溯到V6,選擇V6的鄰接點,而是選擇了V3->V2,因爲V3->V2的距離爲5,而V6->V5的距離爲6。重複上述步驟,直至圖f所示(U = V)。

算法步驟:

首先我們選擇鄰接矩陣存儲圖中各頂點間的關係。使用一個標記數組flag,標記加入U內的頂點。爲了避免重複掃描,可以藉助兩個數組一個記錄U內頂點到V-U內頂點的最短距離,另一個記錄V-U內頂點到U內的最鄰接點。

  1. 初始化上述各數組,存儲圖的二維矩陣每個元素初始化爲無窮大。標記數組全爲false(表示U內無頂點)。記錄距離和鄰接點的數組全爲0。
  2. 給出圖中各頂點之間的關係,且給出開始頂點
  3. 根據開始頂點更新U到V-U內各頂點的距離(鄰接矩陣對應行與各鄰接點之間的關係),以及記錄鄰接點的數組
  4. 找出記錄距離數組中到U到V-U內頂點的最小值,對應頂點加入U,更新U到V-U內頂點的距離關係和鄰接點關係
  5. 重複步驟4,直至U = V
代碼示例:
#include<iostream>
using namespace std;

#define MAX_SIZE 100
#define INT 1e7
int graph[MAX_SIZE][MAX_SIZE];
bool flag[MAX_SIZE];
int close[MAX_SIZE];
int low[MAX_SIZE];

void init_array(){
 	for(int i = 0; i < MAX_SIZE; i++){
  		for(int j = 0; j < MAX_SIZE; j++)
   			graph[i][j] = INT;
 	}
}
void init_graph(int m){
 	for(int i = 0; i < m; i++){
  		int x, y, z;
 	 	cin >> x >> y >> z;
  		graph[x][y] = z;
  		graph[y][x] = z;
 	}
}
void prim(int n, int s){
 	flag[s] = true;
 	for(int i = 1; i <= n; i++){
  		if(i != s){
   			close[i] = 1;
   			low[i] = graph[s][i];
  		} 
 	}
 	for(int i = 1; i <= n; i++){
  		int min = INT, t = -1;
  		for(int j = 1; j <= n; j++){
   			if(!flag[j] && low[j] < min){
    				min = low[j];
    				t = j;
   			}
  		} 
  		if(t == s) return;
  		flag[t] = true;
  		for(int j = 1; j <= n; j++){
   			if(!flag[j] && graph[t][j] < low[j]){
    				low[j] = graph[t][j];
    				close[j] = t;
   			}
  		}
 	} 
}
int calculate_weight(int n){
 	int sum = 0;
 	for(int i = 1; i <= n; i++)
  		sum += low[i];
 	return sum;
}

int main(){
 	int n, m, s;
 	cin >> n >> m >> s;
 	init_array();
 	init_graph(m);
 	prim(n, s);
 	calculate_weight(n);
 	return 0;
}

觀察代碼,每次查找最小值要O(n)的時間,此外共需要執行n次,因此時間複雜度爲O(n^2)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章