当前位置: 首页 > news >正文

【初识数据结构】CS61B中的最小生成树问题

本教程总结CS61B 关于图章节中的最小生成树(Minimum Spanning Trees, MST)问题,以及对应的的算法

什么是最小生成树(MST)

考虑这样一个问题,给你一个无向图,你能不能找出这个图中的一组边,让其满足:

  • 连通的
  • 无环的
  • 涉及到了所有的结点
    比如这样一张图
    alt text
    这样的一组边构成的树,称为生成树
    而让这个树中所有的边权重之和最小即为最小生成树

如何找到最小生成树

一个非常有用的性质:割边属性(Cut Property)

  • 一个 cut 就是将一个图中的结点分成两个非空集合
  • 一个 crossing cut 就是从一个集合结点连接到了另一个集合结点的边
    割边属性是这样描述的:给定任意一个 cut,最小权重的 crossing edge 一定在最小生成树中
    alt text
    比如上面这张图中,所有的红色边都是 crossing edge,其中最小的边一定在最小生成树中
    我们判断 crossing edge 只需要看这个边是否连接了两个属于不同集合(也就是不同颜色)的结点即可

通用的寻找 MST 算法

基于割边属性,我们可以这样构造一个算法:
首先一开始MST没有边:

  • 找到一个没有 crossing edge 在MST中的 cut
  • 然后将最小权重的 crossing edge 加入 MST
  • 一直重复到 V-1 条边

Prim’s Algorithm

相比较最短路径树而言,最小生成树并不需要给出一个起点,在最小生成树中,只需要给你图然后说告诉我哪些边触及所有顶点且权重之和最小即可。

但是并不代表我们不选取一个起始结点,相对来说,Prim 算法会随机选择一个起始结点,这个随机性并不对最终结果产生影响(我们总要寻找一个结点来入手)

算法流程

从一个随机结点开始:

  • 将一个一头连接已经在MST中结点的最短的边,加入MST,直到 V-1 条边

alt text
比如在这个图中,所有符合上述条件的即为红色的边:连接了一个在MST中的结点
然后我们选取这些边中最短的边,也就是A->B或者E->D

Prim 算法实际上是不断地使用了割边属性

改进的 Prim’s Algorithm

Prim 算法是可以奏效的,但效率太低了,因为你必须考虑大量的穿过这个 cut 的 crossing edge
我们对 Prim 算法进行改进:

  • 将所有结点放入 fringe PQ 中,以结点到树的距离作为排序标准
  • 重复:将距离树最近的结点移出,然后将其指出的所有边进行relaxation,然后对 distTo 和 edgeTo 数组进行更新

alt text
这个图中我们刚刚把距离树最近的结点E移出,加入到MST中,然后relax它的所有的边,同时更新 distTo 和 edgeTo 数组

图中加粗的边为MST中的边,图中的虚线表示relax出来的边,作为MST的候选边,如果在relax中发现这条边不如之前的边(比如C->B),或者这条边被后续的边所取代(比如下一步的C->F即将被E->F取代),那么这条边我们甚至不标注为虚线

而如果在MST中的边被新边取代,那么我们把新边加入MST,原来的边回到最开始的模样(不加粗,也不标为虚线)

同时我们看到 Fringe 中的结点是按照到树的距离进行排序的,同时 distTo 数组也变为了到树的距离

算法实现

public PrimMST{public PrimMST(EdgeWeightedGraph G){edgeTo = new Edge[G.V()];distTo = new double[G.V()];marked = new boolean[G.V()];fringe = new SpecialPQ<Double>[G.V()];distTo[s] = 0.0;setDDistancesToInfinityExceptS(s);insertAllVertices(fringe);while(!fringe.isEmpty()){int v = fringe.delMin();scan(G,v);}} ....private scan(EdgeWeightedGraph G, int v){marked[v] = true;for(Edge e: G.adj(v)){int w = e.other(v);if(marked[w]){continue;}if(e.weight() < distTo[w]){distTo[w] = e.weight();edgeTo[w] = e;pq.decreasePriority(w, distTo[w]);}}}
}

Kruskal’s Algorithm

按照权重顺序考虑所有的边,只要这条边加入MST后不构成环,那么就将它加入MST中,重复直到 V-1 条边

Kruskal 算法计算过程中创造出的MST可能是割裂不连续的,但是没关系,最后会得出正确的结果

我们可以通过维护两个辅助数据结构来帮助我们实现:

  • WQU 加权快速集合:帮助我们判断是否有环生成
  • MST :统计最小生成树的边

alt text
在这个图中我们按顺序从 Fringe 中取出对应的边,当我们即将取出E->B这条边时,WQU告诉我们已经有了一条从E到B的路径,也就是下一步即将成环,所以我们不考虑E->B这条边

算法实现

public class KruskalMST{private List<Edge> mst = new ArrayList<Edge>();public KruskalMST(EdgeWeighedGraph G){MinPQ<Edge> pq = new MinPQ<Edge>();for(Edge e: G.edges()){pq.insert(e);}WeighedQuickUnionPC uf = new WeighedQuickUnionPC(G.V());while(!pq.isEmpty()){Edge e = pq.delMin();int v = e.from();int w = e.to();if(!uf.connected(v,w)){uf.union(v,w);mst.add(e);}}}
}
http://www.lryc.cn/news/594698.html

相关文章:

  • Car Kit重构车机开发体验,让车载应用开发驶入快车道
  • 【PTA数据结构 | C语言版】拓扑排序
  • OR条件拆分:避免索引失效的查询重构技巧
  • 【web自动化】-5- fixture集中管理和项目重构
  • 2024年ASOC SCI2区TOP,基于Jaya算法的粒子滤波器用于非线性模型贝叶斯更新,深度解析+性能实测
  • 代码随想录算法训练营第二十七天
  • 为什么 tcp_syncookies 不能取代半连接队列?
  • 【前端】jszip+file-saver:多个视频url下载到zip、页面预加载视频、预览视频、强制刷新视频
  • Python并发编程:突破GIL枷锁,高效利用多核CPU
  • 服务器系统时间不准确怎么办?
  • PHP反序列化漏洞详解
  • 4 种更新的方法将消息从安卓传输到 Mac
  • 2025三掌柜赠书活动第二十五期 网络安全应急响应实战
  • 2025年终端安全管理系统的全方位解析,桌面管理软件的分析
  • 基于python django的BOSS直聘网站计算机岗位数据分析与可视化系统,包括薪酬预测及岗位推荐,推荐算法为融合算法
  • 【设计模式】迭代器模式 (游标(Cursor)模式)
  • Netty实现单通道并发读写,即多路复用
  • Spring MVC 核心工作流程
  • 二、SpringBoot-REST开发
  • OSS文件上传(三):断点续传
  • CentOS 系统上部署一个简单的 Web 应用程序
  • Git上传与下载GitHub仓库
  • 计算机网络:概述层---计算机网络的性能指标
  • FastMCP全篇教程以及解决400 Bad Request和session termination的问题
  • 网络服务(第三次作业)
  • 果园里的温柔之手:Deepoc具身智能如何重塑采摘机器人的“生命感知”
  • GoLand安装指南
  • QT6 源,七章对话框与多窗体(5) 文件对话框 QFileDialog 篇二:源码带注释
  • Android 默认图库播放视频没有自动循环功能,如何添加2
  • 文远知行推出与联想共研的100%车规级HPC 3.0计算平台