求最小生成树(Prim算法与Kruskal算法与并查集)

news2024/11/24 6:59:46

目录

        • 1、案例要求
        • 2、算法设计与实现
          • 2.1 Prim算法
            • 2.1.1 构造无向图
            • 2.1.2 编写Prim算法函数
            • 2.1.3 实现代码
            • 2.1.4 运行结果截图
          • 2.2 Kruskal算法
            • 2.2.1 构造无向图
            • 2.2.2 编写并查集UnionFind类
            • 2.2.3 编写Kruskal算法
            • 2.2.4 实现代码
            • 2.2.5 运行结果截图
        • 3、总结


1、案例要求

利用贪心算法思想,求无向图的最小生成树(分别完成Prim算法、Kruskal算法,其中,Kruskal算法要求使用并查集检查回路)

假设给定的无向图如下:

在这里插入图片描述

2、算法设计与实现

2.1 Prim算法
2.1.1 构造无向图

利用二维数组构造无向图,各路径设置为双向权值相同,不通的路径权值设置为最大整数。

2.1.2 编写Prim算法函数

lowcost数组记录当前最小生成树到其余点最短距离,初始状态为其他顶点到根节点的距离,mst记录记录其余顶点到最小生成树的边所经过顶点,初始化为1,遍历根节点以外的节点,找到距离二叉树最近且未在树中的顶点,将其加入,判断其余顶点从该新顶点到二叉树的距离是否更短,更短则更新lowcost数组和mst数组,直到所有顶点被遍历完。

2.1.3 实现代码

时间复杂度:O(n2)(n为顶点数)

public static int Prim(int[][] graph, int m) {
    //设置两个数组
    // lowcost:记录当前最小生成树到其余点最短距离
    // mst:记录其余顶点到最小生成树的边所经过顶点,如mst[i]=j表示顶点i到最小生成树的边是i->j
    int[] lowcost = new int[m + 1];
    int[] mst = new int[m + 1];
    //记录最小生成树的和
    int sum = 0;

    //数组初始化
    for (int i = 2; i <= m; i++) {
        //顶点1到其余顶点距离
        lowcost[i] = graph[1][i];
        //mst初始化为1
        mst[i] = 1;
    }
    //第一个顶点已被遍历
    mst[1] = 0;

    //第二个点开始
    for (int i = 2; i <= m; i++) {
        int min = Integer.MAX_VALUE;
        int minid = 0;
        for (int j = 2; j <= m; j++) {
            //找到距离二叉树最近的顶点且未在树中
            if (lowcost[j] < min && mst[j] != 0) {
                min = lowcost[j];
                //记录顶点位置
                minid = j;
            }
        }
        //打印路径
        System.out.println(mst[minid] + "->" + minid + " " + min);
        sum += min;
        lowcost[minid] = 0;
        mst[minid] = 0;

        for (int j = 2; j <= m; j++) {
            //判断从新顶点到二叉树的距离是否更短
            if (graph[minid][j] < lowcost[j]) {
                //更新
                lowcost[j] = graph[minid][j];
                mst[j] = minid;
            }
        }
    }
    return sum;
}
2.1.4 运行结果截图

在这里插入图片描述

2.2 Kruskal算法
2.2.1 构造无向图

构造一个Edge类,有起点、终点、权值三个属性,重写compareTo函数,并用list列表存储无向图的各边。

2.2.2 编写并查集UnionFind类

union(x,y)函数实现合并功能,将节点x的父节点设置为节点y;find(x)函数实现查找功能,遍历查找x的父节点直到根节点,返回该根节点,为减少时间复杂度,同时记录查找过程的所有父节点,将所有节点的父节点设置为根节点,以便日后遍历查找耗费过多时间。

2.2.3 编写Kruskal算法

首先由边权值对边进行排序,从小到大,通过find(x)函数判断边上的两个点的根节点是否相同(判断加入后是否形成闭环),不同则加入最小生成树的边集中,同时通过union(x,y)对该边两顶点进行合并,最后直到最小生成树边集的边数==顶点数-1。

2.2.4 实现代码

时间复杂度:O(nlog2n)

并查集代码:

import java.util.HashSet;
import java.util.Set;

//并查集
public class UnionFind {
    //实现查功能
    public static UFNode find(UFNode x) {
        UFNode p = x;
        Set<UFNode> path = new HashSet<>();
        //记录向上追溯的路径上的点
        while (p.parent != null) {
            path.add(p);
            p = p.parent;
        }
        //这些点的parent全部指向这个集的代表(他们共同的老大)
        //优化:第一次未生效,后续生效,后续只需找一次就能找到当前节点的根节点
        for (UFNode ppp : path) {
            ppp.parent = p;
        }
        return p;

    }

    //实现并功能
    public static void union(UFNode x, UFNode y) {
        //将x作为y的父结点
        find(y).parent = find(x);
    }

    //定义静态内部类,这是并查集中的结点
    public static class UFNode {
        UFNode parent;//父结点
    }

}

边类代码:

public class Edge<T> implements Comparable<Edge> {
    private T start;
    private T end;
    private int distance;

    public Edge(T start, T end, int distance) {
        this.start = start;
        this.end = end;
        this.distance = distance;
    }

    public T getStart() {
        return start;
    }

    public void setStart(T start) {
        this.start = start;
    }

    public T getEnd() {
        return end;
    }

    public void setEnd(T end) {
        this.end = end;
    }

    public int getDistance() {
        return distance;
    }

    public void setDistance(int distance) {
        this.distance = distance;
    }

    @Override
    public String toString() {
        return start + "->" + end + " " + distance;
    }

    @Override
    public int compareTo(Edge o) {
        return distance > o.getDistance() ? 1 : (distance == o.getDistance() ? 0 : -1);
    }
}

Kruskal算法代码:

public class Kruskal {
    private final List<Edge> edgeList;//存放图中的所有边
    private final int n;//总顶点数

    private Set<Edge> T = new HashSet<>();//存放生成树的边
    private Map pntAndNode = new HashMap();//边上的每个顶点都和并查集中有与之对应的node

    private Set<Edge> getT() {
        buildMST();
        return T;
    }

    public Kruskal(List<Edge> edgeList, int n) {
        this.edgeList = edgeList;
        //为每个顶点建立一个并查集的点
        for (Edge edge : edgeList) {
            pntAndNode.put(edge.getStart(), new UnionFind.UFNode());
            pntAndNode.put(edge.getEnd(), new UnionFind.UFNode());
        }
        this.n = n;
    }

    //构造一个边表
    private static List<Edge> build() {
        List<Edge> li = new ArrayList<>();
        li.add(new Edge("1", "2", 2));
        li.add(new Edge("1", "6", 8));
        li.add(new Edge("1", "4", 5));
        li.add(new Edge("2", "3", 7));
        li.add(new Edge("2", "4", 7));
        li.add(new Edge("2", "5", 2));
        li.add(new Edge("3", "5", 3));
        li.add(new Edge("4", "5", 6));
        li.add(new Edge("4", "6", 7));
        li.add(new Edge("4", "7", 3));
        li.add(new Edge("5", "7", 4));
        li.add(new Edge("6", "7", 4));

        return li;
    }

    private void buildMST() {
        //先对边集进行排序
        Collections.sort(edgeList);
        for (Edge e : edgeList) {
            //寻找每条边上两个结点在map集合中映射的UFNode
            UnionFind.UFNode x = (UnionFind.UFNode) pntAndNode.get(e.getStart());
            UnionFind.UFNode y = (UnionFind.UFNode) pntAndNode.get(e.getEnd());
            if (UnionFind.find(x) == UnionFind.find(y))
                continue;//如果两个结点来自同一顶点集,则跳过这条边,否则会形成回路
            UnionFind.union(x, y);
            //把边加入到T中
            T.add(e);
            if (T.size() == n - 1)
                return;//生成树的边数==总顶点数-1,表示所有的点已经连接
        }
    }

    public static void main(String[] args) {
        List<Edge> edgeList = build();
        Kruskal obj = new Kruskal(edgeList, 7);
        //getT中就调用了buildMST方法,将生成的边放到了集合中
        for (Edge e : obj.getT())
            System.out.println(e);
    }

}
2.2.5 运行结果截图

在这里插入图片描述

3、总结

贪心算法的基本思想:每次总选择最优情况,这次的prim算法是每次总选择距离最小生成树最近的边,kruskal算法是对边集进行排序后总选择最短边。

以及复习了并查集的相关知识,其中关键的两个函数union和find分别实现的合并功能与查找功能,在查找是还可以通过相关优化算法减少时间复杂度,如父节点更新法与加权标记法,在本案例中使用了父节点更新法。

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

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

相关文章

CameraLink 高清医学影像分析模块

FMC-XM202是一款基于FMC接口标准的1路CameraLink Full模式&#xff08;或者2路CameraLink Base模式&#xff09;采集、1路HDMI&#xff08;DVI&#xff09;视频输出的子卡模块&#xff0c;该模块具有2个CameraLink端口&#xff08;SDR&#xff0c;26PIN&#xff09;&#xff0c…

简单线性线性回归

文章目录 brief直线回归的一般形式参数计算y观测值和回归值的关系基本前提假定假设检验直线回归的变异来源自由度问题&#xff1a;假设检验 多元线性回归 brief 当研究两个有因果关系的变量时&#xff0c;我们希望建立一个方程式表示两者的关系&#xff0c;这样有一个变量得知…

公司里的5种人,建议马上开除

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 多年前&#xff0c;马云在某期湖畔大学开学演讲时&#xff0c;说了一句经典名言&#xff1a;“小公司的成败在于你聘请什么样的人&#xff0c;大公司的成败在于你开除什么样的人。”K哥觉得&#xff0…

Ocean Optics USB2000光谱仪无法在Win10/8系统运行

1、问题描述 USB2000型光谱仪&#xff0c;由于生产年代过于久远&#xff0c;虽然能被Win10系统识别&#xff0c;但是驱动程序安装完成后依然报错&#xff0c; 提示&#xff1a;该设备无法启动。&#xff08;代码 10&#xff09; 请求USB BOS 描述符失败。 运行SpectraSuite软件…

操作系统原理 —— 什么是信号量,信号量如何实现进程互斥、进程同步?(十五)

在之前的章节中&#xff0c;我们提到了进程互斥&#xff0c;以及进程互斥实现的几种方式&#xff0c;那么今天我们再来讲解一种&#xff0c;基于 信号量 来实现进程之间的同步、互斥的方式。 用户进程可以通过使用操作性提供的一对原语来对信号量进行操作&#xff0c;从而很方…

C语言基础知识:C语言中的指针

目录 1、为什么需要指针? 2、指针是什么&#xff1f; 3、指针与变量的关系 4、指针的分类 5、指针的用法 6、指针的运算 7、野指针 8、指针使用时的注意事项 同C语言中其他变量一样&#xff0c;把指针也可以看成是一种变量。不过&#xff0c;这种变量专门存储地址值。…

vscode链接远程服务器开发c++项目

因为要在linux环境下开发c应用&#xff0c;需要一个比较好用的远程工具。之前做深度学习的时候一直用vscode链接服务器写python&#xff0c;感觉用起来很舒服。 vscode下载安装这些就略过了&#xff0c;从插件安装和配置文件开始介绍 参考文章&#xff1a;https://zhuanlan.zh…

如何做一份精致的性能测试报告?

相比于普通的功能测试&#xff0c;性能测试对测试工程师的技能要求更高&#xff0c;一般来说&#xff0c;也只有中高级测试工程师才会有机会做性能测试。 对于题主关心的问题&#xff0c;我拆分出下面三个部分来做解答&#xff1a; 1、性能测试报告的目的 2、性能测试过程中的关…

【算法分析与设计报告】快递终端送货配送系统、基因序列比较、地图染色、文章查重系统、果园篱笆问题(附源码)

一、快递终端送货分配问题 问题描述 假设某快递终端投递站&#xff0c;服务n个小区&#xff0c;小区与快递点之间有道路相连&#xff0c;如下图&#xff0c;边上的权值表示距离。 图1-1 小区快递点图 现在设有m包裹&#xff0c;每个包裹都有自己的目的地及总量。 假设送货员一…

IPB072N15N3G-ASEMI代理英飞凌高压MOS管IPB072N15N3G

编辑&#xff1a;ll IPB072N15N3G-ASEMI代理英飞凌高压MOS管IPB072N15N3G 型号&#xff1a;IPB072N15N3G 品牌&#xff1a;英飞凌 封装&#xff1a;TO-263 最大漏源电流&#xff1a;31A 漏源击穿电压&#xff1a;600V RDS&#xff08;ON&#xff09;Max&#xff1a;99mΩ…

第四届“中国法研杯”司法人工智能挑战赛-刑期预测赛道三等奖方案

一、前言 本文将回顾第四届“中国法研杯”司法人工智能挑战赛-刑期预测算法赛道比赛。使用多任务预训练、然后进行微调的形式最终在比赛中取得了三等奖的成绩。 二、任务介绍 主办方在第一届“中国法研杯”比赛上提出了刑期预测任务&#xff0c;本届将针对往届刑期预测准确率…

《终身成长》笔记六——称赞努力的过程,也将其与结果关联

目录 经典摘录 成为好父母好老师 成长型思维模式的真伪 第一种错误理解&#xff1a;很多人将他们身上某些他们喜欢的优点称作“成长型思维模式” 第二种错误理解&#xff1a;很多人认为成长型思维模式只关乎努力&#xff0c;特别是去夸奖别人的努力 第三种错误理解&#xff…

基于树莓派4B的智能家居

基于树莓派4B的智能家居 前言C语言的简单工厂模式工厂模式介绍类和对象工厂模式的优缺点优点缺点 智能家居框架产品工厂卫生间灯设备二楼灯设备餐厅灯设备客厅灯设备泳池灯设备风扇设备锁设备警报器设备地震监测设备火灾监测设备温湿度检测设备 指令工厂语音控制设备server控制…

如何创建样本手册?

第一步&#xff1a;提前研究和规划 首先明确目标客户群体在其中扮演的角色。 谁会穿你的衣服&#xff1f;您品牌的潜在客户是谁&#xff1f;他们的愿望是什么&#xff1f;他们会被什么打动&#xff1f;设置客户角色至关重要&#xff0c;因为它将决定样本手册的基调&#xff0…

Simulink 自动代码生成电机控制:模型仿真速度的优化

目录 方法一 Simulationmode 方法二 多核并行 方法三 Performance Advisor 总结 方法一 Simulationmode 执行下面的指令获取Simulink仿真实时&#xff0c;这里以霍尔FOC的模型为例&#xff0c;在切换模式为Accelerator时不能使用调用子模型的形式&#xff0c;需要把子模型复制…

map的forEach区别

map的forEach区别 先总结下&#xff1a; map和forEach区别是&#xff1a; 1.map有返回值而且必须return返回一个数组才行 ; 而forEach没有返回值可直接打印结果&#xff1b; 即&#xff1a;forEach()方法不会返回执行结果&#xff0c;而是undefined。也就是说&#xff0c;forEa…

vue 在线聊天实战范例(含选择发送表情、图片、视频、音频,自定义右键快捷菜单,一键复制,左右聊天气泡)

最终效果 完整代码 index.vue <template><div class"page"><div class"leftBox"><h1>访客</h1><div class"chatBox"><div class"chatRecordBox"><div v-for"(item, index) in cha…

DBCO-COOH分子量:305.3,CAS:1353016-70-2,二苯基环辛炔-羧基;类似有DBCO-NH2、SH、MAL、NHS等等

中文名称&#xff1a;二苯基环辛炔-羧基 英文名称&#xff1a;DBCO-acid 英文别称&#xff1a;DBCO-COOH cas: 1353016-70-2 分子式&#xff1a;C19H15NO3 分子量&#xff1a;305.3 DBCO-COOH是DBCO 衍生化的常用构件&#xff0c;在EDC、DCC和HATU等活化剂存在下&#xf…

linux kernel menuconfig kconfig makefile

概述 menuconfig是Linux平台用于管理代码工程、模块及功能的实用工具。 menuconfig的使用方式通常是在编译系统之前在系统源代码根目录下执行make menuconfig命令从而打开一个图形化配置界面&#xff0c;再通过对各项的值按需配置从而达到影响系统编译结果的目的。 Nuttx的me…

Spring Boot 数据库操作Druid和HikariDataSource

目录 Spring Boot 数据库操作 应用实例-需求 创建测试数据库和表 进行数据库开发&#xff0c; 在pom.xml 引入data-jdbc starter 参考官方文档 需要在pom.xml 指定导入数据库驱动 在application.yml 配置操作数据源的信息 创建bean\Furn.java 测试结果 整合Druid 到…