U4_2:图论之MST/Prim/Kruskal

2023-11-29 15:04
文章标签 图论 prim kruskal mst u4

本文主要是介绍U4_2:图论之MST/Prim/Kruskal,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、最小生成树-MST
    • 生成MST策略
      • 一些定义
    • 思路
    • 彩蛋
  • 二、普里姆算法(Prim算法)
    • 思路
    • 算法流程
      • 数据存储
        • 分析
    • 伪代码
    • 时间复杂度分析
  • 三、克鲁斯卡尔算法(Kruskal算法)
    • 分析
    • 算法流程
      • 并查集-Find-set
    • 伪代码
    • 时间复杂度分析

一、最小生成树-MST

无向图,无环,所有点连通,边权重和最小
(没有权重标注就默认为1)
在这里插入图片描述

生成MST策略

  1. 从一个空图开始。
  2. 尝试一次添加一条边,始终确保所构建的保持无循环。
  3. 如果在添加了每条边之后,我们确定生成的图是某个最小生成树的子集,我们就完成了。

一些定义

集合 A A A是最小生成树 T T T的子集,当 A U ( u , v ) A\space U(u,v) A U(u,v)也是 M S T MST MST子集时, ( u , v ) (u,v) (uv)是安全的。
切割 c u t cut cut ( S , V − S ) (S,V-S) (S,VS)
a a a c u t cut cut r e s p e c t s respects respects a a a s e t set set A A A o f of of e d g e s edges edges i f if if n o no no e d g e s edges edges i n in in A A A c r o s s e s crosses crosses t h e the the c u t cut cut.
An edge is a light edge crossing a cut if its weight is the minimumof any edge crossing the cut
在这里插入图片描述

思路

(S, V - S) be any cut of G that respects A
(u, v) be a light edge crossing the cut (S, V - S)Then, edge (u, v) is safe for A.
则 lt means that we can find a safe edge by

  1. first finding a cut that respects A
  2. then finding the light edge crossingthat cut
    That light edge is a safe edge

彩蛋

本质上下面所要讲的Prim算法和Kruskal算法都是依据这个总思路来的,先分隔cut,然后根据cut找light edge,最后不断生成MST

二、普里姆算法(Prim算法)

思路

  1. 首先选择任意顶点r作为树的根。
  2. 当树不包含图中的所有顶点时:找到离开树的最短边并将其添加到树中。
    这个思路可以想到,每次的cut就是选入作为顶点的集合 S S S和未选入的顶点 G − S G-S GS

算法流程

数据存储

区分cut:最初始是空集,所有顶点被标记为白色,选入的顶点标记为黑色
利用优先队列存储
利用优先队列(小顶堆)去寻找 t h e the the l i g h e s t lighest lighest e d g e edge edge(相应函数如下)
3. I n s e r t ( u , k e y ) Insert(u, key) Insert(u,key):用键值key在Q中插入u。
4. u = E x t r a c t − m i n ( ) u = Extract- min() u=Extractmin():提取键值最小的项。
5. D e c r e a s e − K e y ( u , n e w − k e y ) Decrease-Key(u, new-key) DecreaseKey(u,newkey):将u的键值减小为new-key
利用 p r e d [ A ] pred[A] pred[A]去存储每个顶点的存储顺序

分析

t h e the the l i g h e s t lighest lighest e d g e edge edge本质上是在黑白分界点的这些边中寻找,因此每次更新都需要维护这些点( k e y key key)。
初始的时候设为 i n i f i n i t y inifinity inifinity,每次加入新顶点时就找到它的所有边判断是否比现在的key是否更小了,如果更小了就可以更新并且换前驱
在这里插入图片描述

伪代码

for u ∈ V docolor[u] ← white,key[u] ← +∞
end
key[u] ← 0,pred[r] ← null;	//最开始的顶点
Q ← new PriQueue(V)   
while Q is  noempty dou ← Q.Extract-Min(); //the lighest edge   for v ∈ adj[u] doif(color[u] ← white && w[u,v] < key[u]) thenkey[u] ← w[u,v]Q.decrease-Key(v,key[u]) pred[v] ← uendendcolor[u] ← black
end

时间复杂度分析

创建优先队列 O ( V l o g V ) O(VlogV) O(VlogV),每次循环 E x t r a c t − M i n Extract-Min ExtractMin l o g ( V ) log(V) log(V),总共V个顶点,总时间复杂度为 O ( V l o g V ) O(VlogV) O(VlogV)。每次循环 D e c r e a s e − K e y Decrease-Key DecreaseKey O ( l o g V ) O(logV) O(logV),因为循环内每次更新都是针对边来说,所有边都遍历一遍,因此循环内总时间复杂度为 O ( E l o g V ) O(ElogV) O(ElogV),总时间复杂度为 T ( n ) = O ( ( V + E ) l o g V ) = O ( E l o g V ) T(n)=O((V+E)logV)=O(ElogV) T(n)=O((V+E)logV)=O(ElogV)

三、克鲁斯卡尔算法(Kruskal算法)

分析

  1. 从一个空图开始。
  2. 尝试一次添加一条边,始终确保所构建的保持无循环。.
  3. 如果我们在每一步都确定生成的图是某个最小生成树的子集,我们就完成了。

与Prim的算法生长一棵树不同,Kruskal的算法生长一组树(森林)。
最初,这个森林只由顶点组成(没有边)。
在每一步中,添加不产生循环的权重最小的边。
继续直到森林“合并”成一棵树。

本质上,也是继承于一说的主算法:
设A为Kruskal算法选择的边集,设(u, v)为下一步要添加的边。这足以说明这一点:
t h e r e there there i s is is a a a c u t cut cut t h a t that that r e s p e c t s respects respects A A A
( u , v ) (u, v) (u,v) i s is is t h e the the l i g h t light light e d g e edge edge c r o s s i n g crossing crossing t h i s this this c u t cut cut
在这里插入图片描述

算法流程

  1. 刚开始 A A A为空集, F F F存入所有边并且从小到大排序,
  2. 在F中选择一条权值最小的边e,检查将e加到A上是否形成一个循环。
    构成循环,则从F移除
    不构成循环,则从F添加进A
  3. F为空集时停止操作

现在有个问题,怎么才能不形成环呢,
在框架算法的每一步中, ( V , A ) (V,A) (V,A)都是非循环的,因此它是一个森林,一个顶点延申两条枝干,且枝干之间没有路径,这样就是森林。因此:
如果 u u u v v v在同一棵树中,则将边 u , v {u,v} u,v添加到A中创建一个循环。
如果 u u u v v v不在同一棵树中,那么将边 u , v {u,v} u,v添加到 A A A中不会创建一个循环。

根据这个性质,如果一条边被选中,它的两个端点若在一个树上,那么再将这条边添加进树时,肯定会形成环,根据这一性质,我们可以维护并查集去判断是否成环

并查集-Find-set

本质上,并查集就是一个个树集合,每个元素都唯一指向它的父亲,根节点父亲就是子集,因此每棵树的唯一标识就是根节点。如果两个元素唯一标识一样,那它们就在一棵树上。
在这里插入图片描述

j u d g e judge judge f i n d − s e t ( u ) find-set(u) findset(u) = = == == f i n d − s e t ( v ) find-set(v) findset(v),维护 f i n d − s e t find-set findset过程如下:

  1. C r e a t e − s e t u ) Create-set u) Createsetu):创建包含单个元素 u u u的集合。 O ( 1 ) O(1) O(1)
x.parent ← x
  1. F i n d − s e t ( u ) Find-set (u) Findset(u):查找包含元素u的集合(假设每个集合都有唯一的ID,后面可知是树的根节点)。 O ( l o g n ) O(logn) O(logn)
while x != x.parent dox ← x.parent
end
  1. U n i o n ( u , v ) Union(u, v) Union(u,v):将分别包含u和v的集合归并为一个公共集合。(当判断完不会形成环后,可以合并). O ( l o g n ) O(logn) O(logn)(找树的根节点费时,其他都是 O ( 1 ) O(1) O(1)时间)
    注意当我们将两棵树合并在一起时,我们总是将高树的根作为矮树的父树。不然会很畸形,费时。
    如果两棵树有相同的高度,我们选择第一棵树的根指向第二棵树的根。树的高度增加了1(根节点+被合并的子树,因此高度+1)。其他情况下树的高度都是不变的。
a ← Find-Set(x)
b ← Find-Set(y)
if a.height <= b.height thenif a.height is equal to b.height thenb.hright++;enda.parent ← b
end
elseb.parent ← a
end

伪代码

sort E in increasing order by weight w;
A ← {}
for u ∈ V doCreate-Set(u);
end
for ei ∈E do  //ei两个端点为ui,viif(find-set(ui)!=find-set(vi)) thenadd {ui,vi} to AUnion(ui,vi)end
end
return 

时间复杂度分析

排序用时 O ( E l o g E ) O(ElogE) O(ElogE) c r e a t e − s e t create-set createset用时 O ( V ) O(V) O(V),循环次数是边的次数 E E E,每次循环 u n i o n union union花费 l o g ( V ) log(V) log(V),总时间复杂度 O ( E l o g V ) O(ElogV) O(ElogV),因此总花费 T ( n ) = O ( E l o g E ) T(n)=O(ElogE) T(n)=O(ElogE)(边比顶点多,取大的)

这篇关于U4_2:图论之MST/Prim/Kruskal的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/433287

相关文章

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

C语言-数据结构 克鲁斯卡尔算法(Kruskal)邻接矩阵存储

相比普里姆算法来说,克鲁斯卡尔的想法是从边出发,不管是理解上还是实现上都更简单,实现思路:我们先把找到所有边存到一个边集数组里面,并进行升序排序,然后依次从里面取出每一条边,如果不存在回路,就说明可以取,否则就跳过去看下一条边。其中看是否是回路这个操作利用到了并查集,就是判断新加入的这条边的两个顶点是否在同一个集合中,如果在就说明产生回路,如果没在同一个集合那么说明没有回路可以加入

【代码随想录训练营第42期 续Day52打卡 - 图论Part3 - 卡码网 103. 水流问题 104. 建造最大岛屿

目录 一、做题心得 二、题目与题解 题目一:卡码网 103. 水流问题 题目链接 题解:DFS 题目二:卡码网 104. 建造最大岛屿 题目链接 题解:DFS  三、小结 一、做题心得 也是成功补上昨天的打卡了。 这里继续图论章节,还是选择使用 DFS 来解决这类搜索问题(单纯因为我更熟悉 DFS 一点),今天补卡的是水流问题和岛屿问题。个人感觉这一章节题对于刚

图论篇--代码随想录算法训练营第五十二天打卡| 101. 孤岛的总面积,102. 沉没孤岛,103. 水流问题,104.建造最大岛屿

101. 孤岛的总面积 题目链接:101. 孤岛的总面积 题目描述: 给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。 现在你需要计算所有孤岛的总面积,岛屿面积的计算方式为组成岛屿的陆地的总数。 解题思路: 从周边找到陆地,然后通过 dfs或者bfs 将

代码随想录 刷题记录-28 图论 (5)最短路径

一、dijkstra(朴素版)精讲 47. 参加科学大会 思路 本题就是求最短路,最短路是图论中的经典问题即:给出一个有向图,一个起点,一个终点,问起点到终点的最短路径。 接下来讲解最短路算法中的 dijkstra 算法。 dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。 需要注意两点: dijkstra 算法可以同时求 起点到所有节点的最短路径权值不

图论篇--代码随想录算法训练营第五十一天打卡| 99. 岛屿数量(深搜版),99. 岛屿数量(广搜版),100. 岛屿的最大面积

99. 岛屿数量(深搜版) 题目链接:99. 岛屿数量 题目描述: 给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。 解题思路: 1、每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 2、遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都

图论篇--代码随想录算法训练营第五十天打卡| 深度优先搜索理论基础,98. 所有可达路径,广度优先搜索理论基础

深度优先搜索理论基础 DFS模板: void dfs(参数) {if (终止条件) {存放结果;return;}for (选择:本节点所连接的其他节点) {处理节点;dfs(图,选择的节点); // 递归回溯,撤销处理结果}} 98. 所有可达路径 题目链接:98. 所有可达路径 题目描述: 给定一个有 n 个节点的有向无环图,节点编号从 1 到 n。请编写一个函数,找出并返回所