图的最小生成树的理解和实现: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条边的连通图. 而最小生成树就是最小权 ...
随机推荐
- Ubuntu开机自动挂载分区
虽然我基本上都是使用Linux的,但是仍然有些时候需要切换到Windows(双系统),如果所有的分区都使用ext4等Linux分区格式,则在Windows下访问十分不方便. 因此,我一般会将一些两个系 ...
- css position说明
absolute 生成绝对定位的元素,选择第一个position不等于 static 定位的第一个父元素进行定位. 元素的位置通过 "left", "top", ...
- python类和元类
python 类和元类详解 小麦麦子 2016-09-06 11:11:00 今天在网上看到一篇关于python语言中类和元类(metaclass)的一些讲解和简单运用,感觉对pyth ...
- vs与qt
http://blog.csdn.net/woniuye/article/details/54928477 1. #include "qmessagebox.h" QMessage ...
- sqlserver怎么将excel表的数据导入到数据库中
在数据库初始阶段,我们有些数据在EXCEL中做好之后,需要将EXCEL对应列名(导入后对应数据库表的字段名),对应sheet(改名为导入数据库之后的表名)导入指定数据库, 相当于导入一张表的整个数据. ...
- JSP中的一个树型结构
看方力勋的javaWeb,采用左右值来表示树型结构(就是俺门的多级分类)表结构 页面代码 <%@ page language="java" import="java ...
- nodename nor servname provided, or not known
mac来使用redis然后产生上述错误,据说是用户名的问题 解决: 打开终端: cat /private/etc/hosts sudo vi /private/etc/hosts 将错误的那个名字加入 ...
- MySQL性能调优与架构设计——第1章 MySQL 基本介绍
第1章 MySQL 基本介绍 前言:作为最为流行的开源数据库软件之一, MySQL 数据库软件已经是广为人知了. 但是为了照顾对MySQL还不熟悉的读者,这章我们将对 MySQL 做一个简单的介绍.主 ...
- IntelliJ IDEA 2016.1.3(64) license server 与汉化
license server:http://idea.iteblog.com/key.php 汉化:将resources_cn.jar 复制到安装IDEA安装目录下的lib文件夹中.重新打开即可. r ...
- ADB server didn't ACK问题,连上手机问题(转)
出现如下情况 ADB server didn't ACK* failed to start daemon * 解决办法: 方法一: (1)查看任务管理器,关闭所有adb.exe,或者运行->cm ...