图的最小生成树的理解和实现:Prim和Kruskal算法
最小生成树
一个连通图的生成树是一个极小的连通子图,它含有图中所有的顶点,但只有足以构成一棵树的n-1条边。我们将构造连通网的最小代价生成树称为最小生成树(Minimum Cost Spanning Tree)。
普利姆算法(Prim)
定义
假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:
在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。
普利姆算法在运行中始终保持TE中的边集构成一棵生成树。
一个例子(该示例参见:https://www.cnblogs.com/PJQOOO/p/3855017.html)
1. 在进行Prim算法时,先选择一个顶点作为起始点,一般情况下选则v1,设U集合为当前所找到最小生成树里面的顶点,TE集合为所找到的边。此时状态为:
U={v1}; TE={};
2. 现在查找一个顶点在U集合中,另一个顶点在V-U集合中的最小权值,如下图,在红线相交的线上找最小值
通过图中我们可以看到边v1-v3的权值最小为1,那么将v3加入到U集合,(v1,v3)加入到TE,状态如下:
U={v1,v3}; TE={(v1,v3)};
3. 继续寻找,现在状态为U={v1,v3}; TE={(v1,v3)};在与红线相交的边上查找最小值。我们可以找到最小的权值为(v3,v6)=4,那么我们将v6加入到U集合,并将最小边加入到TE集合,那么加入后状态如下:
我们可以找到最小的权值为(v3,v6)=4,那么我们将v6加入到U集合,并将最小边加入到TE集合,那么加入后状态如下:
U={v1,v3,v6}; TE={(v1,v3),(v3,v6)}
如此循环一下直到找到所有顶点为止。
4. 下图像我们展示了全部的查找过程:
代码实现(Java):
package 最小生成树;
public class Prim {
private final int INFINITY = 65535;
private int MAXVEX;
public int[][] initArray() {
//模拟无向图的邻接矩阵
int maze[][] = {
{ 0, 10,INFINITY, INFINITY, INFINITY, 11, INFINITY, INFINITY, INFINITY },
{ 10, 0, 18, INFINITY, INFINITY, INFINITY, 16, INFINITY, 12 },
{ INFINITY, INFINITY, 0, 22,INFINITY,INFINITY,INFINITY,INFINITY, 8 },
{ INFINITY, INFINITY, 22, 0, 20, INFINITY, INFINITY, 16, 21 },
{ INFINITY, INFINITY, INFINITY, 20, 0, 26, INFINITY, 7, INFINITY},
{11, INFINITY, INFINITY, INFINITY, 26,0, 17, INFINITY, INFINITY},
{INFINITY, 16, INFINITY,INFINITY,INFINITY, 17, 0, 19, INFINITY},
{INFINITY, INFINITY,INFINITY, 16, 7, INFINITY, 19, 0, INFINITY},
{INFINITY, 12, 8,21,INFINITY,INFINITY,INFINITY,INFINITY,0}
};
this.MAXVEX = maze.length;
return maze;
}
public void MiniSpanTreePrim(int[][] G) {
int min, j, k;
int[] adjvex = new int[this.MAXVEX];//保存相关顶点下标
int[] lowcost = new int[this.MAXVEX];//保存相关顶点间边的权值
lowcost[0] = 0; //初始化第一个权值为0 ,即V0加入生成树
adjvex[0] = 0; //初始化第一个顶点下标为0
for (int i = 1; i < this.MAXVEX; i++) {
lowcost[i] = G[0][i];
adjvex[i] = 0;
}
for (int i = 1; i < this.MAXVEX; i++) {
min = INFINITY;
j = 1; k = 0;
while(j < G.length ) {
if(lowcost[j] != 0 && lowcost[j] < min) {
min = lowcost[j];
k = j;
}
j++;
}
System.out.println("(" + adjvex[k] + "," + k + ")");
lowcost[k] = 0;
for (j = 1; j < this.MAXVEX; j++) {
if(lowcost[j] != 0 && G[k][j] < lowcost[j]) {
lowcost[j] = G[k][j];
adjvex[j] = k;
}
}
}
}
public static void main(String[] args) {
Prim prim = new Prim();
int[][] g = prim.initArray();
prim.MiniSpanTreePrim(g);
}
}
克鲁斯卡尔算法(Kruskal)
在了解Kruskal算法前,我们需要了解的数据结构有:
边集数组
边集数组是由两个一维数组构成。一个是存储顶点的信息,一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标、终点下标、权组成。
并查集
并查集主要用在判断一个图中的两个顶点是否能相联通的问题。可以参见这篇博客:https://blog.csdn.net/w1085541827/article/details/52076481
这里不做赘述。
右边的就是该图的边集数组,以边为单位,存储着这条边的首尾两个顶点,还有这条边的权值,这里注意的是对于顶点的前后顺序是没有关系的,比如第一行的(0, 1),也可以存成(1, 0)。边集数组以边为基项,有几条边就有几项。
在边集数组右边的就是并查集,并查集是一个用双亲表示法所表示的森林,利用森林来查找某个顶点的根节点是哪个,如果两个顶点的根节点相同,证明两个顶点在同一颗树中,那么以这两个顶点构成的边就不能出现在最小生成树中。通过并查集,我们来确定在生成树中加上一条边后是否会形成环。并查集以顶点为基项,有几个顶点就有几项。
步骤:
1. 对图的存储边集数组进行排序,按权重值由小到大进行排序。
2. 初始化并查集,每一个位置中的值初始化为其对应下标(也可以初始化每一个位置为零)
3. 选取边集数组中最小项(第一项),查询该边所对应的顶点在并查集中是否同源。
若同源:则跳过,继续向下遍历存储结构
若不同源:将该边加入生成树中,将该边的结尾顶点放入下标为起点并查集中,即修改后者的根在并查集中位置的值为前者的 根。
代码实现(Java):
package 最小生成树;
public class Kruskal {
private final int INFINITY = 65535;
private int numEdges = 15;
class Edges {
int begin;
int end;
int weight;
public Edges (int begin, int end, int weight) {
this.begin = begin;
this.end = end;
this.weight = weight;
}
}
public void MiniSpanTreeKruskal(int[][] G) {
int n, m, e = 0;
Edges[] edges = new Edges[numEdges];
int[] parent = new int[G.length];
for (int i = 0; i < G.length; i++) {
for (int j = 0; j < i; j++) {
if(G[i][j] != 0 && G[i][j] < INFINITY) {
Edges ed = new Edges(i, j, G[i][j]);
edges[e] = ed;
e++;
}
}
}
for (int i = 0; i < G.length; i++) {
parent[i] = 0;
}
for (int i = 0; i < numEdges; i++) {
n = Find(parent, edges[i].begin);
m = Find(parent, edges[i].end);
if(m != n) {
parent[n] = m;
System.out.println("(" + edges[i].begin + "," + edges[i].end + "," + edges[i].weight + ")");
}
}
}
private int Find(int[] parent, int f) {
while(parent[f] > 0) {
f = parent[f];
}
return f;
}
public static void main(String[] args) {
Prim prim = new Prim();
int[][] g = prim.initArray();
Kruskal k = new Kruskal();
k.MiniSpanTreeKruskal(g);
}
}
图的最小生成树的理解和实现:Prim和Kruskal算法的更多相关文章
- 算法(图论)——最小生成树及其题目应用(prim和Kruskal算法实现)
题目 n个村庄间架设通信线路,每个村庄间的距离不同,如何架设最节省开销? Kruskal算法 特点 适用于稀疏图,时间复杂度 是nlogn的. 核心思想 从小到大选取不会产生环的边. 代码实现 代码中 ...
- 无向带权图的最小生成树算法——Prim及Kruskal算法思路
边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...
- [数据结构]最小生成树算法Prim和Kruskal算法
最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总 ...
- 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...
- prim和kruskal算法
//邻接矩阵 int n,G[MAXV][MAXN]; int d[MAXV];//表示到树的距离 bool vis[MAXV]={false}; int prim(){ fill(d,d+MAXV, ...
- ZOJ 1542 POJ 1861 Network 网络 最小生成树,求最长边,Kruskal算法
题目连接:problemId=542" target="_blank">ZOJ 1542 POJ 1861 Network 网络 Network Time Limi ...
- [从今天开始修炼数据结构]图的最小生成树 —— 最清楚易懂的Prim算法和kruskal算法讲解和实现
接上文,研究了一下算法之后,发现大话数据结构的代码风格更适合与前文中邻接矩阵的定义相关联,所以硬着头皮把大话中的最小生成树用自己的话整理了一下,希望大家能够看懂. 一.最小生成树 1,问题 最小生成树 ...
- 【2018寒假集训Day 8】【最小生成树】Prim和Kruskal算法模板
Luogu最小生成树模板题 Prim 原理与dijkstra几乎相同,每次找最优的点,用这个点去松弛未连接的点,也就是用这个点去与未连接的点连接. #include<cstdio> #in ...
- 图论——最小生成树:Prim算法及优化、Kruskal算法,及时间复杂度比较
最小生成树: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.简单来说就是有且仅有n个点n-1条边的连通图. 而最小生成树就是最小权 ...
随机推荐
- 28-python 中格式对齐之中文格式对齐问题
一般的可以按这个搞,但是中文就会有问题: python基础_格式化输出(%用法和format用法) 对于 print('1234567890' * 10)print('%10s' % '今天好')pr ...
- zip多个分割文件合并
copy /b 1.z01+1.z02+1.zip 注意:1.zip在最后
- css控制两个表格的边线重合
控制两个表格的边线重合 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:/ ...
- 视频转换工具ffmpeg
安装ffmpeg ffmpeg官网下载地址点击此处. 如果使用mac也可用homebrew下载安装:brew install ffmpeg 使用ffmpeg 命令如下:ffmpeg -i input. ...
- meterpreter命令
meterpreter详解与渗透实战 基本命令: background quit shell irb client.sys.config.sysinfo() 调用windows API:client. ...
- IO 之 InputStream 和 Reader
package java.io; // Representing an input stream of bytes. public abstract class InputStream impleme ...
- string 转换为枚举对应的值
public static Object Parse(Type enumType,string value) 例如:(Colors)Enum.Parse(typeof(Colors), "R ...
- GNOME桌面的安装
首先搭建yum仓库 http://www.cnblogs.com/jw35/p/5967677.html yum grouplist #列出yum仓库里的软件组 ...
- centos 7.6 开机报错信息(一):welcome to emergency mode!
welcome to emergency mode!after logging in ,type "journalctl -xb" to view system logs,&quo ...
- UnicodeEncodeError:'latin-1' codec can't encode characters in position 0-1: ordinal not in range(256)
Scrapy爬虫向数据库写入数据时报错: UnicodeEncodeError:'latin-1' codec can't encode characters in position 0-1: ord ...