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

目录

        • 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.mfbz.cn/a/21520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

低代码与其拓荒,不如颠覆开发行业

目录 一、前言 二、低代码是一个值得信赖的“黑盒子” 粗略总结&#xff0c;开发者对低代码平台所见即所得设计器有两种反应&#xff1a; 三、人人都爱黑盒子 四、用“低代码平台”来开发是什么样的感受&#xff1f; 五、结论 一、前言 在科幻电影中&#xff0c;我们看到…

【OpenCV】C++红绿灯轮廓识别+ROS话题实现

目录 前言 一、背景知识 Opencv轮廓检测 ROS相关知识 二、环境依赖 三、具体实现 Step1&#xff1a;初始化ROS&#xff0c;订阅话题 Step2&#xff1a;接收话题&#xff0c;进入回调 1. 帧处理 2. 膨胀腐蚀处理 Step3&#xff1a;红绿特征处理 1. 提取绘制轮廓 2…

【网络协议详解】——数据链路层协议(学习笔记)

&#x1f4d6; 前言&#xff1a;数据链路层是 OSI 模型中的第二层&#xff0c;位于物理层之上&#xff0c;是通信网络中的重要组成部分之一。数据链路层协议负责将网络层传输的数据分组封装成帧&#xff0c;传输到物理层&#xff0c;并通过物理介质进行传输。同时&#xff0c;数…

算法笔记:A2-A4-RSRQ切换算法

1 LTE 切换 LTE切换是移动通信网络中的一个过程&#xff0c;移动设备在保持无间断服务的情况下&#xff0c;将其连接从一个基站切换到另一个基站。当移动设备离开当前基站的覆盖范围或网络资源拥塞时&#xff0c;就需要进行切换。LTE切换通常是基于特定的条件触发的&#xff0…

makefile 学习(1):C/C++ 编译过程

1. GCC 介绍 1.1 介绍 GCC 官方文档 https://gcc.gnu.org/onlinedocs/ 官方文档是最权威的&#xff0c;网上所有的答案都来自官方文档国内论坛参差不齐&#xff0c;找到好的答案比较花时间&#xff0c;并且很容易被错误的文档误导。所以推荐看官方文档靠谱点&#xff0c;并且…

二、数据字典开发

文章目录 二、数据字典开发1、搭建service-cmn模块1.1 搭建service-cmn模块1.2 修改配置1.3 启动类 2、数据字典列表2.1 数据字典列表接口2.1.1 model模块添加数据字典实体2.1.2 添加数据字典mapper2.1.4 添加数据字典controller 2.2 数据字典列表前端2.2.1 添加路由2.2.2 定义…

【Java算法题】剑指offer_01数据结构

前言 刷题链接&#xff1a; https://www.nowcoder.com/exam/oj/ta?page2&tpId13&type265 1. 链表 JZ24 反转链表 思路&#xff1a;基本操作&#xff0c;如下所示。 /* public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} }…

ad18学习笔记一

如何自学altium designer如何自学altium designer&#xff1f; - 知乎如何自学altium designer 这里面有ad官方推荐的b站的视频&#xff1a;可以直接去b站关注ad官方账号 AltiumChina&#xff0c;它本身就发布了很多实用教程。 在知乎的这个界面也有Altium Designer Ver18_官…

c++ 11标准模板(STL) std::set(六)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class set;(1)namespace pmr { template <class Key, class Compare std::less<Key>> using se…

如何使用SCQA模型提高表达能力

SCQA架构是“结构化表达”工具。 一、什么是“SCQA架构”&#xff1f;‍ S&#xff08;Situation&#xff09;情景——由熟悉的情境或事实引入 C&#xff08;Complication&#xff09;冲突——指出实际面临的困境或冲突 Q&#xff08;Question&#xff09;疑问——你如何分析…

文本三剑客正则表达式3

文章目录 文本三剑客&正则表达式31 awk工作原理2 awk的基本格式及其内置变量2.1 基本格式2.2 内置变量2.3 示例2.3.1 直接打印所有内容2.3.2 取每一行的第一列2.3.3 打印行号&#xff0c;及所有内容2.3.4 打印第三行2.3.5 打印2-4行2.3.6 打印第2行和第4行2.3.7 用正则表达…

基于harbor安装私有镜像仓库

目录 Harbor介绍 Harbor安装 下载完成后&#xff0c;在压缩包解压到/usr/local目录下&#xff1a; 修改Harbor配置文件 推送本地镜像到harbor上 1、给本地镜像打一个标签 2、 设置docker的daemon.json 3、重启docker 4、使用docker登录harbor 5、把本地的镜像push到harbor…

银豆信息张雪灿:钻石级合作伙伴的增长秘诀

编者按&#xff1a; 杭州银豆信息技术有限公司&#xff08;简称“银豆”&#xff09;&#xff0c;是一家专注于云计算服务的高科技企业&#xff0c;目前已为2000家企业级客户提供了专业的行业解决方案, 与人民网、光大银行、长安汽车金融、vivo金融、浙江省农科院、淄博市大数…

MediaPipe虹膜检测:实时虹膜跟踪和深度估计

包括计算摄影(例如,人像模式和闪光反射)和增强现实效果(例如,虚拟化身)在内的大量实际应用都依赖于通过跟踪虹膜来估计眼睛位置。一旦获得了准确的光圈跟踪,我们就可以确定从相机到用户的公制距离,而无需使用专用的深度传感器。反过来,这可以改善各种用例,从计算摄影…

机器学习之SVM分类器介绍——核函数、SVM分类器的使用

系类文章目录 机器学习算法——KD树算法介绍以及案例介绍 机器学习的一些常见算法介绍【线性回归&#xff0c;岭回归&#xff0c;套索回归&#xff0c;弹性网络】 文章目录 一、SVM支持向量机介绍 1.1、SVM介绍 1.2、几种核函数简介 a、sigmoid核函数 b、非线性SVM与核函…

从内网护卫到零信任尖兵:腾讯iOA炼成记

腾讯既是企业产品的服务商又是使用者&#xff0c;很多产品最原始的出发点最早只是为了解决腾讯自身某一个需求&#xff0c;经过不断地发展完善和业务场景锤炼&#xff0c;最终进化成一个成熟的企服产品。本系列文章讲述的是这样一组Made in Tencent故事&#xff0c;这是系列的第…

Word批量更改图片环绕方式与=尺寸大小

前提&#xff1a;一份Word文档里面有100张图片&#xff0c;有大有小&#xff0c;需要将100张图片更改为统一大小&#xff0c;宽度与高度均为5厘米&#xff0c;同时环绕方式也需要改成四周型。 默认Word图片的默认环绕方式为嵌入型&#xff0c;需要统一更改为四周型&#xff0c;…

linux 安装 maven 3.8 版本

文章目录 1&#xff1a;maven 仓库官网 2、下载安装包 3、使用&#xff1a;Xftp 上传到你想放的目录 4、解压文件 ​编辑 5、配置环境变量 ​编辑 6、刷新 /etc/profile 文件 7、查看maven 版本 1&#xff1a;maven 仓库官网 Maven – Download Apache Mavenhttps://mave…

Java 基础进阶篇(十五):IO 流总结(全网最全面)

文章目录 前置内容&#xff1a;字符集一、IO 流概述二、字节流2.1 文件字节输入流 FileInputStream2.1.1 案例&#xff1a;每次读取一个字节2.1.2 案例&#xff1a;每次读取一个字节数组2.1.3 案例&#xff1a;读取文件的全部字节 2.2 文件字节输出流 FileOutputStream2.3 文件…

使用Docker Dockerfile构建php LNMP集成开发环境,并运行Thinkphp5

宿主机环境 系统&#xff1a;MAC、Windows10 Docker版本&#xff1a;Docker version 23.0.5 Docker Desktop:Dockerdesktop官方地址 前言 这篇主要介绍如何在Mac、Windows10使用docker搭建LNMP集成开发环境。下面我会写Dockerfile编译安装Nginxphp基础环境。mysql、redis基…
最新文章