当前位置: 首页 > 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.dtcms.com/a/291211.html

相关文章:

  • LLaMA-Factory 微调可配置的模型基本参数
  • jcmd用法总结
  • 完整的 SquareStudio 注册登录功能实现方案:已经烧录到开发板正常使用
  • 83、形式化方法
  • Unity VR多人手术系统恢复3:Agora语音通讯系统问题解决全记录
  • 【CAN】01.CAN简介硬件电路
  • 视网膜分支静脉阻塞(BRVO)及抗VEGF治疗的多模态影像学研究
  • 同步与异步?从一个卡顿的Java服务说起
  • 文字检测到文字识别
  • 如何用 Z.ai 生成PPT,一句话生成整套演示文档
  • 自反馈机制(Self-Feedback)在大模型中的原理、演进与应用
  • 【PTA数据结构 | C语言版】哥尼斯堡的“七桥问题”
  • 【ROS1】07-话题通信中使用自定义msg
  • (9)机器学习小白入门 YOLOv:YOLOv8-cls 技术解析与代码实现
  • 选择排序 冒泡排序
  • LinkedList与链表(单向)(Java实现)
  • android studio 远程库编译报错无法访问远程库如何解决
  • 算法提升之字符串回文问题-(马拉车算法)
  • Java基础教程(011):面向对象中的构造方法
  • 模拟高负载测试脚本
  • Flink框架:keyBy实现按键逻辑分区
  • 250kHz采样率下多信号参数设置
  • mysql-5.7 Linux安装教程
  • 无人机报警器技术要点与捕捉方式
  • Anaconda 路径精简后暴露 python 及工具到环境变量的配置记录 [二]
  • Linux学习之Linux系统权限
  • scratch音乐会开幕倒计时 2025年6月中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析
  • Git核心功能简要学习
  • 知识 IP 的突围:从 “靠感觉” 到 “系统 + AI” 的变现跃迁
  • 网络编程及原理(八)网络层 IP 协议