【算法基础实验】图论-最小生成树-Prim的即时实现

news2024/11/26 4:37:08

理论知识

Prim算法是一种用于计算加权无向图的最小生成树(MST, Minimum Spanning Tree)的贪心算法。最小生成树是一个连通的无向图的子图,它包含所有的顶点且总权重最小。Prim算法从一个起始顶点开始,不断将权重最小的边加入生成树,直到包含图中的所有顶点为止。

关键思想

  • 起点选择:从任意一个顶点开始,将其标记为已访问。
  • 贪心选择:从已访问的顶点集合中,选择一条连接到未访问顶点的权重最小的边,将该边加入MST。
  • 更新过程:重复选择和标记过程,直到所有顶点都包含在MST中。

时间复杂度

  • 使用简单的实现(如使用无序数组)时,Prim算法的时间复杂度为O(V^2)。
  • 使用优先队列(如最小堆)优化时,时间复杂度可以降低到O(E log V),其中E是边的数量,V是顶点的数量。

算法流程

要改进 LazyPrimMST,可以尝试从优先队列中删除失效的边,这样优先队列就只含有树顶点和非树顶点之间的横切边。关键在于,我们感兴趣的只是连接树顶点和非树顶点中权重最小的边。当我们将顶点 v 添加到树中时,对于每个非树顶点 w 产生的变化只可能使得 w 到最小生成树的距离更近了,如图所示。简而言之,我们不需要在优先队列中保存所有从 w 到树顶点的边——而只需要保存其中权重最小的那条,在将 v 添加到树中后检查是否需要更新这条权重最小的边(因为v-w 的权重可能更小)。我们只需遍历 v 的邻接链表就可以完成这个任务。换句话说,我们只会在优先队列中保存每个非树顶点 w 的一条边:将它与树中的顶点连接起来的权重最小的那条边。将 w 和树的顶点连接起来的其他权重较大的边迟早都会失效,所以没必要在优先队列中保存它们。

PrimMST 类使用了索引优先队列实现的 Prim 算法。它将 LazyPrimMST 中的 marked[] 和 mst[] 替换为两个顶点索引的数组edgeTo[] 和 distTo[],它们具有如下性质。

  • 如果顶点 v 不在树中但至少含有一条边和树相连,那么 edgeTo[v] 是将 v 和树连接的最短边,distTo[v] 为这条边的权重。
  • 所有这类顶点 v 都保存在一条索引优先队列中,索引 v 关联的值是 edgeTo[v]的边的权重。

这些性质的关键在于优先队列中的最小键即是权重最小的横切边的权重,而和它相关联的顶点 v 就是下一个将被添加到树中的顶点。marked[] 数组已经没有必要了,因为判断条件 !marked[w] 等价于 distTo[w] 是无穷的(且 edgeTo[w] 为 null)。要维护这些数据结构,PrimMST 会从优先队列中取出一个顶点 v 并检查它的邻接链表中的每条边 v-w。如果 w 已经被标记过,那么这条边就已经失效了;如果 w 不在优先队列中或者 v-w 的权重小于目前已知的最小值 edgeTo[w],代码会更新数组,将 v-w 作为将 w 和树连接的最佳选择。
在这里插入图片描述

以下流程是基于本实验数据描绘的处理轨迹图:

解释流程图有助于对代码的理解,理解核心原理
关注数据的组织方式

在节点中:

  • 白色圆形是树节点,灰色圆形是非树节点,

在边中:

  • 红线代表当前加入优先队列的边

  • 粗红线代表队列中权重最小的边

  • 粗黑线代表已经纳入MST中的边

  • 灰色线代表可以被其他线平替的线

💡 也就是说当我们遍历到一个非树节点时,需要判断当前遍历到的边和该非树节点已经被树遍历到的边相比,哪个最小,不是最小的都会被标记为灰色。如何记录树到非树节点的距离呢?是使用ditsTo[非树节点]这个数组来完成的

在Index列中:

  • 红色数字代表节点已经被遍历
  • 黑色数字代表该节点已经被纳入MST中了,marked置为True
  • 灰色数字表示节点尚未被遍历

💡 遍历动作一定是由一个新晋的树节点发起的
当树节点遍历到了另一个树节点则直接跳过,根据切分定理,我们要遍历的是非树节点

在edgeTo列中:

  • 黑色数字代表树节点
  • 灰色数字代表非树节点
  • 当边的两个节点都加入MST后,连接符号“-”由红色变为黑色

💡 新加入的边一定包含树节点,另外一端肯定要指向某个非树节点,非树节点是灰色的数字,这里的灰色数字一定是跟index值保持一致的,所以我们可以根据非树节点的编号作为索引来管理PQ队列,我们的处理核心是与树直连的非树节点

在distTo列中:

  • 红色代表尚未确定边是否可以纳入MST
  • 黑色代表确定该边已经加入了MST,并且已经从PQ中删除
  • 红色箭头代表当前PQ中值最小的边

在这里插入图片描述

实验数据

8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93

代码实现

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;

public class myPrimMST {
    private myEdge[] edgeTo;
    private double[] distTo;
    private boolean[] marked;
    private myIndexMinPQ<Double> pq;

    private double totalWeight;

    public myPrimMST(myEdgeWeightedGraph G)
    {
        edgeTo = new myEdge[G.V()];
        distTo = new double[G.V()];
        marked = new boolean[G.V()];
        pq = new myIndexMinPQ<>(G.V());
        for(int v=0;v<G.V();v++)
            distTo[v] = Double.POSITIVE_INFINITY;
        distTo[0] = 0.0;
        pq.insert(0,0.0);
        while(!pq.isEmpty())
            visit(G,pq.delMin());
    }

    private void visit(myEdgeWeightedGraph G, int v)
    {
        marked[v] = true;
        for(myEdge e: G.adj(v))
        {
            int w = e.other(v);
            if(marked[w]) continue;
            if(e.weight()<distTo[w])
            {
                edgeTo[w] = e;
                distTo[w] = e.weight();
                if(pq.contains(w)) pq.change(w,distTo[w]);
                else pq.insert(w,distTo[w]);
            }
        }
    }
    public Iterable<myEdge> edges()
    {
        myBag<myEdge> mst = new myBag<myEdge>();
        for(int v=1;v< edgeTo.length;v++)
            mst.add(edgeTo[v]);
        return mst;
    }

    public double weight()
    {
        totalWeight = 0.00;
        for(myEdge e:edges())
        {
            totalWeight +=e.weight();
            StdOut.println(totalWeight);
        }
        return totalWeight;
    }

    public static void main(String[] args)
    {
        In in = new In(args[0]);
        myEdgeWeightedGraph G = new myEdgeWeightedGraph(in);
        myPrimMST mst = new myPrimMST(G);

        for(myEdge e:mst.edges())
            StdOut.println(e);
        StdOut.printf("%.5f\n", mst.weight());
    }
}

代码详解

类定义和成员变量

java复制代码
public class myPrimMST {
    private myEdge[] edgeTo;  // 记录最小生成树的边
    private double[] distTo;  // 记录从树到该顶点的最小权重边
    private boolean[] marked; // 记录顶点是否在树中
    private myIndexMinPQ<Double> pq; // 索引优先队列,帮助找到最小权重的边

    private double totalWeight; // 最小生成树的总权重

  • edgeTo:存储每个顶点的连接边,形成MST。
  • distTo:存储到每个顶点的最小边权重。
  • marked:标记哪些顶点已经被包括在MST中。
  • pq:索引优先队列,用于动态查找最小边。

构造函数

java复制代码
public myPrimMST(myEdgeWeightedGraph G) {
    edgeTo = new myEdge[G.V()];
    distTo = new double[G.V()];
    marked = new boolean[G.V()];
    pq = new myIndexMinPQ<>(G.V());
    for(int v=0; v<G.V(); v++)
        distTo[v] = Double.POSITIVE_INFINITY;
    distTo[0] = 0.0;
    pq.insert(0, 0.0);
    while(!pq.isEmpty())
        visit(G, pq.delMin());
}

  • 初始化:edgeTodistTomarkedpq
  • 设置初始顶点的距离为 0 并将其插入优先队列。
  • while 循环:每次从优先队列中删除权重最小的顶点,并调用 visit 方法处理该顶点。

visit方法

java复制代码
private void visit(myEdgeWeightedGraph G, int v) {
    marked[v] = true;
    for(myEdge e: G.adj(v)) {
        int w = e.other(v);
        if(marked[w]) continue;
        if(e.weight() < distTo[w]) {
            edgeTo[w] = e;
            distTo[w] = e.weight();
            if(pq.contains(w)) pq.change(w, distTo[w]);
            else pq.insert(w, distTo[w]);
        }
    }
}

  • visit 方法标记顶点 v 为已访问。
  • 遍历所有与 v 连接的边,并检查另一端顶点 w 是否已在MST中。
  • 如果找到更小的连接权重边,则更新 edgeTodistTo,并在优先队列中更新或插入 w

edges方法和weight方法

java复制代码
public Iterable<myEdge> edges() {
    myBag<myEdge> mst = new myBag<>();
    for(int v=1; v<edgeTo.length; v++)
        mst.add(edgeTo[v]);
    return mst;
}

public double weight() {
    totalWeight = 0.00;
    for(myEdge e : edges())
        totalWeight += e.weight();
    return totalWeight;
}

  • edges() 方法返回MST中的所有边。
  • weight() 方法计算并返回MST的总权重。

main方法

java复制代码
public static void main(String[] args) {
    In in = new In(args[0]);
    myEdgeWeightedGraph G = new myEdgeWeightedGraph(in);
    myPrimMST mst = new myPrimMST(G);

    for(myEdge e : mst.edges())
        StdOut.println(e);
    StdOut.printf("%.5f\n", mst.weight());
}

  • 从文件中读取图数据并构建 myEdgeWeightedGraph 对象 G
  • 使用 myPrimMST 类计算最小生成树 mst
  • 打印最小生成树的所有边和总权重。

总结

Prim算法通过逐步构建最小生成树,并利用优先队列来高效地选择最小权重的边。在这段代码中,myPrimMST 类实现了Prim算法,通过维护一个最小优先队列来管理尚未包括在MST中的顶点,从而动态调整生成树并计算其总权重。

实验步骤

C:\Users\xyz\IdeaProjects\algrithoms\src>javac myPrimMST.java 
C:\Users\xyz\IdeaProjects\algrithoms\src>java myPrimMST data\tinyEWD.txt     
2-7 0.34
6-2 0.40
7-5 0.28
5-4 0.35
1-3 0.29
0-2 0.26
5-1 0.32
2.24000

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2061155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CTFHUB | web进阶 | JSON Web Token | 无签名

一些JWT库也支持none算法&#xff0c;即不使用签名算法。当alg字段为空时&#xff0c;后端将不执行签名验证 开启题目 账号密码随便输&#xff0c;登录之后显示只有 admin 可以获得 flag 在此页面抓包发到 repeater&#xff0c;这里我们需要用到一个 Burp 插件&#xff0c;按图…

科研绘图配色大全

目录 01 颜色网站 1.1 Material 1.1.1 tailwindcolor 1.2 Trending Color Palettes1.3 Material Palette 1.4 Graphs Colors 1.5 RGB颜色值与十六进制颜色码转换 1.6 colorbrewer 1.7 优设 1.8 Chinese Colors1.9 handpicked colors 02 科研绘图配色方案 2.1 常见科技…

干货:2024必备的四大PDF编辑器推荐!

面对PDF文件的编辑需求&#xff0c;你是否感到无从下手&#xff1f;那么&#xff0c;今天就为大家推荐几款实用的PDF编辑工具&#xff0c;让你轻松应对各种PDF编辑难题。 福昕PDF编辑器 链接&#xff1a;editor.foxitsoftware.cn 福昕PDF编辑器多功能专业级是我PDF编辑器。它…

【C++例题 / 训练】二分算法(模板 例题)

引言 二分也就是二分查找&#xff0c;又叫折半查找。这种算法正如其名&#xff0c;每一次都要分一半。 二分算法可以分为二分查找和二分答案。 以在一个升序数组中查找一个数为例&#xff0c;每次考察数组当前部分的中间元素&#xff0c;如果中间元素刚好是要找的&#xff0…

王牌功能 | 法大大“证据管理”,让关键数据坚不可摧!

到底还能不能好好签合同… 法大大证据管理功能&#xff0c;基于电子合同签署全流程&#xff0c;为使用法大大电子签的机构及个人用户提供互联网数据电文的实时存证&#xff0c;并由第三方机构进行证据固化&#xff0c;将存证的数据电文转化成可读性更高、具有司法效力的证明材料…

多线程、多进程,还是异步?-- Python 并发 API 如何选择

如何选择正确的 Python 并发 API模块 &#xff1f; Python 标准库提供了三种并发 API &#xff0c; 如何知道你的项目应该使用哪个 API&#xff1f; 在本教程将带逐步了解各API的特性、区别以及各自应用场景&#xff0c;指导你选择最合适的并发 API。 多线程、多进程&#xff0…

音频采集spring_ws_webrtc (html采集麦克风转gb711并发送广播播放)完整案例

下载地址&#xff1a;http://www.gxcode.top/code 项目说明 springbootwebscoektwebrtc 项目通过前端webrtc采集麦克风声音&#xff0c;通过websocket发送后台&#xff0c;然后处理成g711-alaw字节数据发生给广播UDP并播放。 后台处理项目使用线程池(5个线程)接受webrtc数据并…

JAVA基础:字节字符转换流

前言 当我们使用流读取数据时&#xff0c;如果这个数据中含有中文&#xff0c;我们对这个数据进行操作就会出现乱码问题&#xff0c;这时候我们要使用字节字符转换流来处理一下数据。 字节字符转换流 字节字符转换流是一个过程流 字节字符转换流是一个字符流&#xff0c;所…

如何为 SEO 做关键词优化

关键词优化是一种基本的 SEO 技术&#xff0c;可以提高你的网站在搜索结果中的可见度&#xff0c;并吸引更多的访问者。 在这份关键词优化指南中&#xff0c;我们将分享在 SEO 的这一方面脱颖而出所需的知识、技巧和技巧。使用本指南可以超越您的竞争对手&#xff0c;并为您的…

一文搞懂数据标注

hihi,大家好,最近开始和内容那个团队研究SD出图了,作为家具家电行业的小牛人,除了研究SD和ComfyUI本身,需要了解算法同学如何进行模型微调的,因为用的是开源的底座,预训练部分就不深入介绍了,重点看一下二次预训练,在这个过程中有一个环节比较关注就是小规模数据集,也…

【java】RuoYi-Vue前后端分离版本-请求被拦截,怎么修改拦截过滤器,解决方案

【java】RuoYi-Vue前后端分离版本-请求被拦截&#xff0c;怎么修改拦截过滤器 它用到了一个安全管理框架Spring Security 你可以通过这篇文章《Spring Security 详解》 去了解它&#xff0c;怎么使用或者使用原理。 所有业务都受SecurityConfig配置所过滤 SecurityConfig配置…

2024年思维导图工具怎样可以轻松选择

思维导图&#xff0c;作为一种直观、有效的思维工具&#xff0c;凭借其强大的信息整理能力和创意激发潜力&#xff0c;逐渐成为了人们学习、工作和生活中不可或缺的一部分。今天&#xff0c;就让我们一起走进这个充满智慧的领域&#xff0c;探索那些2024年大家都在使用的思维导…

Cleer耳机好不好用?南卡、Cleer、飞利浦、倍思横评对比

​大家好&#xff0c;作为一位多年的数码博主和耳机发烧友&#xff0c;最近后台收到不少私信&#xff0c;都是小伙伴想让我测评一下最近很火热的开放式耳机&#xff0c;那么为了回馈大家这么久的支持&#xff0c;说做就做&#xff01;我买了最近网上很热门的几款开放式耳机&…

电线 硬线 和软线 连接

电线 硬线 和软线 连接 首先把软线&#xff0c;拧成一股&#xff0c;然后搭到硬线上回转一圈&#xff0c;这里要注意线头往反向回转&#xff0c;大家看下图中的软线&#xff0c; 再在硬线上缠绕7—8圈&#xff0c;根据线的长短&#xff0c;过长就剪去一些。 最后将硬导线往后弯…

【速览】设计模式(更新中)

目录 一、背景二、优缺点优点缺点 三、适用场景四、核心组成创建型模式 Creational Design Pattern 5结构型模式 Structural Design Patterns 7行为型模式 Behavioral Design Patterns 11’ 五、底层原理六、对比参考 一、背景 这个技术出现的背景、初衷和要达到什么样的目标或…

【Python开发实践】在线商城系统——需求及需求分析

项目背景及需求 这个练习项目的设置背景是一家图书销售公司&#xff0c;为了扩大销售渠道&#xff0c;想要开通网上商城&#xff0c;利用在线博客和电子商城来销售图书。 具体需求如下&#xff1a; 每个商品可以留言 实现在线购物车处理和订单处理 实现对产品、购物车和订单的…

python | 图片转换为 pdf 实现方法

目录 一、PIL 库简介及安装使用方法 &#xff08;一&#xff09;python 不同版本下 PIL 的使用方法 二、图片转换为 pdf 的两种实现方法 &#xff08;一&#xff09;简易版——pdf 页面尺寸跟随图片大小 &#xff08;二&#xff09;常用版——pdf 每页尺寸统一为 A4 一、P…

C#PACS系统源码,影像存档与传输系统源码,数字化医学影像系统源码,三维重建影像PACS系统源码

C#PACS系统源码&#xff0c;影像存档与传输系统源码&#xff0c;数字化医学影像系统源码&#xff0c;三维重建影像PACS系统源码 PACS即影像存档与传输系统&#xff08;Picture Archiving and Communication System&#xff09;&#xff0c;是医学影像、数字化图像技术、计算机技…

C++遍历for_each

#include <iostream> #include <stack> #include <vector> #include <algorithm> using namespace std; //打印数组 class bianli { public:void operator()(int val){cout << val << " ";} }; void printVector(int val) {cout…

【经验分享】将ui文件转化成py代码

目录 先写一个简单的ui界面 将ui文件另存为 将UI文件转换为py代码 测试一下你生成的py文件 &#x1f31f; 嗨&#xff0c;我是命运之光&#xff01; &#x1f30d; 2024&#xff0c;每日百字&#xff0c;记录时光&#xff0c;感谢有你一路同行。 &#x1f680; 携手启航&…