图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

时间:2022-09-05 14:38:18

图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 B(G)。其中 T(G)是遍历图时所经过的边的集合,B(G) 是遍历图时未经过的边的集合。显然,G1(V, T) 是图 G 的极小连通子图,即子图G1 是连通图 G 的生成树。

深度优先生成森林

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用  右边的是深度优先生成森林:图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

连通图的生成树不一定是唯一的,不同的遍历图的方法得到不同的生成树;从不同的顶点出发可得到不同的生成树。
连通图本身就是连通分量,其中顶点集+遍历经过的边=生成树。
非连通图的生成森林不一定是唯一的。
非连通图各个连通分量的顶点集+遍历时经过的边=若干颗生成树(生成森林)

最小生成树 
给定一个无向网络,在该网的所有生成树中,使得各边权数之和最小的那棵生成树称为该网的最小生成树。

问题的提出:要在 n 个城市间建立交通网,要考虑的问题如何在保证 n 点连通的前题下最节省经费?

如何求连通图的最小生成树?

构造最小生成树的算法很多,其中多数算法都利用了一种称之为 MST 的性质。

MST 性质:设 N = (V, E)  是一个连通网,U 是顶点集 V 的一个非空子集。若边 (u, v) 是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边 (u, v) 的最小生成树。

方法一:普里姆 (Prim) 算法。

算法思想:

1、设 N=(V, E) 是连通网,TE 是N 上最小生成树中边的集合。初始令 U={u0}, (u0属于V ), TE={ }。
2、在所有 u属于U, v属于V-U 的边 (u, v)属于E 中,

找一条代价最小的边 (u0, v0)。

将 (u0, v0) 并入集合 TE,同时 v0 并入 U。
 
3、
重复上述操作直至 U=V 为止,则 T=(V, TE) 为 N 的最

小生成树。

总得来说,普里姆算法就是以树为单位,找最小的权边,特点是针对无向图!只和顶点有关,和边无关,适用于稠密图。算法时间复杂度为 O(n^2)

如图:普里姆算法求最小生成树

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

初始令 U={u0}, (u0属于V ), TE={ }。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用   图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

在所有 u属于U, v属于V-U 的边 (u, v)属于E 中,找一条代价最小的边 (u0, v0)。将 (u0, v0) 并入集合 TE,同时 v0 并入 U。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用   图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

重复上述操作直至 U=V 为止,则 T=(V, TE) 为 N 的最小生成树。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用    图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

继续

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用    图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

最后,遍历完

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用    图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

Prim算法的实现

顶点集合如何表示?最小边如何选择?一个顶点加入U集合如何表示?如下面的例子:图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
当U集合中加入一个新顶点时,V-U集合中的顶点到U的最小代价边可能会更新,k 代表最终选择的顶点,k=3,代表选择是v3这个顶点,因为1-3代价是最小的=1
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
选取了 v3,之后,继续以最新的树为单位,来找最小的权值边,通过看和哪个顶点连接。
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
k=6,代表选择是v6这个顶点,因为3-6代价是最小的=4,在所有的和最新的树邻接的顶点中,权值最小的边。
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
选取 v6之后
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
继续以最新的树为单位,找临近的顶点,看哪条边的权值最小,找到6-4这条边,权值=2
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
新的树如图
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
继续以最新的树为单位,找临近的顶点,看哪条边的权值最小,找到3-2这条边,权值=5
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
新的树如图
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
继续以最新的树为单位,找临近的顶点,看哪条边的权值最小,找到2-5这条边,权值=3
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
直到所有顶点全部并入生成树之后,程序结束
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
 

方法二:克鲁斯卡尔 (Kruskal) 算法。

使用了并查集,直接从边中找到不成环的最小的权边(最简单的求最小生成树的算法),特点:只针对无向图,包好普里姆算法,都是只针对无向图。

算法思想:

1、设连通网  N = (V, E ),令最小生成树初始状态为只有 n 个顶点而无边的非连通图 T=(V, { }),每个顶点自成一个连通分量。
2、在 E 中选取代价最小的边,若该边依附的顶点落在 T 中不同的连通分量上(即:不能形成环),则将此边加入到 T 中;否则,舍去此边,选取下一条代价最小的边。
3、依此类推,直至 T 中所有顶点都在同一连通分量上为止。

最小生成树可能不惟一(包括普里姆算法都是一样的道理)

把所有的边按照权值升序排列,从最小边开始(不能形成回路),选取,组成最小生成树。直到所有的边并入则结束(不是顶点!)克鲁斯卡尔算法主要在排序边的权值序列的时候最费时间,他的算法时间复杂度和排序算法有关,而排序算法的时间复杂度和图的边 e 有关系,和顶点 v 没有关系。故适用于稀疏图。(而普里姆算法适合稠密图)
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
下面是图解步骤:
按照升序,找出权值的排序序列:1 2 3 4 5 5 5 6 6 6
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
注意选取权值最小的边的时候,不要形成回路
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
按照权值的升序排列的顺序查找选取合适的边
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
继续,按照权值的升序排列的顺序查找选取合适的边
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
注意选取5的时候,避免环的生成,即可
图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
直到所有的边都并入即可。
那么在克鲁斯卡尔算法里,通过找合适的边,该如何避免形成回路呢?换句话说,如何判断是否形成了回路?

使用并查集可以判断是否形成了回路,kruskal算法用到了一种贪心策略,首先要把边集数组以边的权值从小到大排序,然后一条边一条边的查找,如果边的两个端点不在一个集合内,则将此边添加到正在生长的树林中,并合并两个端点所在的集合,直到最小生成树已生成完毕。

并查集:
是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

并查集是一种非常简单的数据结构,它主要涉及两个基本操作,分别为:

A. 合并两个不相交集合

B. 判断两个元素是否属于同一个集合

1)合并两个不相交集合(Union(x,y))

合并操作很简单:先设置一个数组Father[x],在克鲁斯卡尔算法里,需要使用双亲存储结构,表示x的“父亲”的编号。那么,合并两个不相交集合的方法就是,找到其中一个集合最父亲的父亲(也就是最久远的祖先),将另外一个集合的最久远的祖先的父亲指向它。

通俗的说,就是把其中一个树的根,作为另一个树的根结点的一个孩子结点即可。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

上图为两个不相交集合,合并后可以看出:Father(b)=Father(g)=f 结点

2)判断两个元素是否属于同一集合(Find_Set(x)),本操作可转换为寻找两个元素的最久远祖先是否相同。可以采用递归实现。

并查集的优化问题

寻找祖先时,我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度。为了避免这种情况,我们需对路径进行压缩,即当我们经过”递推”找到祖先节点后,”回溯”的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了,如下图所示。可见,路径压缩方便了以后的查找。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

回到克鲁斯卡尔算法,使用并查集来实现判断回路的生成否

比如从 v1开始(一共是 v1、v2、v3、v4、v5、v6),则开始把 v1-v6作为各个单根树,以森林来表示,让每个元素构成一个个的单元素的集合,需要使用数组表示,存储方式就是双亲存储结构(方便找到共同的父亲)。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

每次使用并查集,将后入的边上的另一个结点作为孩子结点,而没有加入的结点还是去做为单根的树:

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

如图所示,上图,该选取权值=5的边了,此时有两个树

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用   和   图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

如果选取3-4或者1-4这两条边的任意一个,单根树是不会产生根相同的情形的,而加入的(作为孩子的根),一定会找到共同祖先的,这样就可以发现回路的存在! 而选取2-3这条边的话,在并查集中,就不会查出共同的祖先,也就是没有环的形成。

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

通俗的说,就是通过两个元素所在的结点推出跟结点,若根相同,则为同一个集合,否则不是同一个集合(也就是不形成回路)

图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用的更多相关文章

  1. 普里姆Prim算法介绍

    普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法. 基本思想 对于图G而言,V是所有顶点的集合:现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T ...

  2. 图的普里姆(Prim)算法求最小生成树

    关于图的最小生成树算法------普里姆算法 首先我们先初始化一张图: 设置两个数据结构来分别代表我们需要存储的数据: lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说 ...

  3. 图解最小生成树 - 普里姆(Prim)算法

    我们在图的定义中说过,带有权值的图就是网结构.一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边.所谓的最小成本,就是n个顶点,用n-1条边把一个连通图连接 ...

  4. JS实现最小生成树之普里姆(Prim)算法

    最小生成树: 我们把构造连通网的最小代价生成树称为最小生成树.经典的算法有两种,普利姆算法和克鲁斯卡尔算法. 普里姆算法打印最小生成树: 先选择一个点,把该顶点的边加入数组,再按照权值最小的原则选边, ...

  5. 普里姆(Prim)算法

    /* 普里姆算法的主要思想: 利用二维数组把权值放入,然后找在当前顶点的最小权值,然后走过的路用一个数组来记录 */ # include <stdio.h> typedef char Ve ...

  6. 洛谷P3366【模板】最小生成树-克鲁斯卡尔Kruskal算法详解附赠习题

    链接 题目描述 如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz 输入输出格式 输入格式: 第一行包含两个整数N.M,表示该图共有N个结点和M条无向边.(N<=5000,M&l ...

  7. 最小生成树-普利姆&lpar;Prim&rpar;算法

    最小生成树-普利姆(Prim)算法 最小生成树 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一种特殊的图),或者 ...

  8. 图论---最小生成树----普利姆&lpar;Prim&rpar;算法

    普利姆(Prim)算法 1. 最小生成树(又名:最小权重生成树) 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一 ...

  9. 图解最小生成树 - 克鲁斯卡尔&lpar;Kruskal&rpar;算法

    我们在前面讲过的<克里姆算法>是以某个顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树的.同样的思路,我们也可以直接就以边为目标去构建,因为权值为边上,直接找最小权值的边来构建生成树 ...

随机推荐

  1. Python&lowbar;Day6&lowbar;反射&lowbar;正则表达式之篇

    一.反射 定义:利用字符串形式去对象(模块)中操作(寻找/检查/删除/设置)成员 #getattr:获取模块中属性 #hasattr:检查模块中是否存在某个成员(函数) #delattr:删除模块中成 ...

  2. 【DSA MOOC】起泡排序的原理及常数优化

    根据学堂在线TsinghuaX: 30240184X 数据结构(2015秋)这门课的内容,对bubblesort做了一些总结. 1. bubblesort(起泡排序),原理来自这样一个观察规律:若序列 ...

  3. &lbrack;转载&rsqb; 高大上的 CSS 效果:Shape Blobbing

    这篇大部分是转载,来自<高大上的 CSS 效果:Shape Blobbing>和 <Shape Blobbing in CSS> 有部分是自己理解和整理,配合效果要做出 app ...

  4. python&lowbar;计算1&plus;……&plus;100中偶数和

    如何计算1+--+100中偶数和? 1. 把奇数去掉,通过if,判断累加数除以2的余数,是否为1,判断是否是奇数 2. 通过continue 跳过对奇数的累加 #!/usr/bin/python3 d ...

  5. SPOJ DIVCNT2 &lbrack;我也不知道是什么分类了反正是数论&rsqb;

    SPOJ DIVCNT2 - Counting Divisors (square) 题意:求 \[ \sum_{i=1}^n\sigma_0(i^2) \] 好棒啊! 带着平方没法做,考虑用其他函数表 ...

  6. Centos Git1&period;7&period;1升级到Git2&period;2&period;1

    安装需求: ># yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel asciidoc ># ...

  7. java知识点集锦--基础知识部分

    1.面向对象特征:封装.继承.多态 封装:把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口来访问.面向对象的本质就是将现实世界描绘成一系列完全自治.封闭的对象.我们在类中编写的方法就是 ...

  8. JS典记

        var href = "";     //遍历a标签     $ ( "a"). each (function () {         href = ...

  9. 682&period; Baseball Game

    static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...

  10. emoji表情存储到数据库的方法

    方案1:修改数据库编码 为什么我们设置表的的字符类型为utf8却不能存放emoji呢?原来utf8可能是2或3或4个字节,而mysql的utf8是3个字节,存放一个emoji是需要4个字节的,自然不够 ...