最小生成树-prim

博客皆个人学习过程中整理,如有问题,欢迎大家指正。
本文链接: https://blog.csdn.net/qq_42017331/article/details/101900459

最小生成树

首先明白两个概念,什么是生成树以及最小生成树?
相关概念引入:

  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)。

猜你喜欢

转载自blog.csdn.net/qq_42017331/article/details/101900459