最短路径与最小生成树:Dijkstra、Prim与Kruskal算法详解

在图论中,最短路径和最小生成树问题是两个重要的课题。本文将介绍三种经典的算法:Dijkstra、Prim和Kruskal,并对它们进行对比分析。我们将讨论这些算法解决的问题、各自的优劣性以及它们之间的异同点,并提供完整的代码示例。

Dijkstra算法

Dijkstra算法用于解决单源最短路径问题,即从图中的一个顶点出发,找到到其他所有顶点的最短路径。

Dijkstra算法的工作原理:
  1. 初始化:将源点到自己的距离设为0,其他所有顶点到源点的距离设为无穷大。
  2. 优先队列:使用一个优先队列存储顶点,按照当前最短路径距离进行排序。
  3. 松弛操作:从优先队列中取出距离最小的顶点,更新与其相邻的顶点的最短路径距离。
  4. 重复:重复上述步骤,直到优先队列为空。
Java代码示例:
import java.util.*;public class Dijkstra {public static int[] dijkstra(Graph graph, int source) {int V = graph.size();int[] distTo = new int[V];boolean[] visited = new boolean[V];Arrays.fill(distTo, Integer.MAX_VALUE);distTo[source] = 0;PriorityQueue<Node> pq = new PriorityQueue<>();pq.add(new Node(source, 0));while (!pq.isEmpty()) {Node node = pq.poll();int v = node.vertex;if (visited[v]) continue;visited[v] = true;for (Graph.Edge edge : graph.getEdges(v)) {int w = edge.to;if (visited[w]) continue;relax(edge, v, w, distTo, pq);}}return distTo;}private static void relax(Graph.Edge edge, int v, int w, int[] distTo, PriorityQueue<Node> pq) {if (distTo[v] + edge.weight < distTo[w]) {distTo[w] = distTo[v] + edge.weight;pq.add(new Node(w, distTo[w]));}}static class Node implements Comparable<Node> {int vertex;int distance;Node(int vertex, int distance) {this.vertex = vertex;this.distance = distance;}@Overridepublic int compareTo(Node other) {return Integer.compare(this.distance, other.distance);}}public static void main(String[] args) {Graph graph = new Graph(5);graph.addEdge(0, 1, 10);graph.addEdge(0, 3, 30);graph.addEdge(0, 4, 100);graph.addEdge(1, 2, 50);graph.addEdge(2, 4, 10);graph.addEdge(3, 2, 20);graph.addEdge(3, 4, 60);int source = 0;int[] distances = dijkstra(graph, source);System.out.println("Vertex\tDistance from Source");for (int i = 0; i < distances.length; i++) {System.out.println(i + "\t" + distances[i]);}}
}
Prim算法

Prim算法用于解决最小生成树(MST)问题,即在一个无向连通图中找到一棵权重最小的生成树。

Prim算法的工作原理:
  1. 初始化:选择任意一个顶点作为起始点,将其距离设为0,其他顶点距离设为无穷大。
  2. 优先队列:使用一个优先队列存储顶点,按照当前最短边的权重进行排序。
  3. 扫描并更新:从优先队列中取出距离最小的顶点,更新与其相邻的尚未加入树的顶点的最短边权重。
  4. 重复:重复上述步骤,直到所有顶点都加入生成树。
Java代码示例:
import java.util.*;public class PrimMST {private Edge[] edgeTo;private double[] distTo;private boolean[] marked;private PriorityQueue<Node> pq;public PrimMST(Graph G, int s) {edgeTo = new Edge[G.size()];distTo = new double[G.size()];marked = new boolean[G.size()];pq = new PriorityQueue<>();Arrays.fill(distTo, Double.POSITIVE_INFINITY);distTo[s] = 0.0;pq.add(new Node(s, 0.0));while (!pq.isEmpty()) {int v = pq.poll().vertex;scan(G, v);}}private void scan(Graph G, int v) {marked[v] = true;for (Graph.Edge e : G.getEdges(v)) {int w = e.to;if (marked[w]) continue;if (e.weight < distTo[w]) {distTo[w] = e.weight;edgeTo[w] = e;pq.add(new Node(w, distTo[w]));}}}static class Node implements Comparable<Node> {int vertex;double distance;Node(int vertex, double distance) {this.vertex = vertex;this.distance = distance;}@Overridepublic int compareTo(Node other) {return Double.compare(this.distance, other.distance);}}public Iterable<Edge> edges() {List<Edge> mst = new ArrayList<>();for (Edge e : edgeTo) {if (e != null) mst.add(e);}return mst;}public double weight() {double weight = 0.0;for (Edge e : edges()) {weight += e.weight;}return weight;}public static void main(String[] args) {Graph graph = new Graph(5);graph.addEdge(0, 1, 10);graph.addEdge(0, 3, 30);graph.addEdge(0, 4, 100);graph.addEdge(1, 2, 50);graph.addEdge(2, 4, 10);graph.addEdge(3, 2, 20);graph.addEdge(3, 4, 60);PrimMST mst = new PrimMST(graph, 0);System.out.println("Edges in MST:");for (Edge e : mst.edges()) {System.out.println(e);}System.out.println("Total weight of MST: " + mst.weight());}
}
Kruskal算法

Kruskal算法也是解决最小生成树问题的另一种算法。它通过逐步添加最小权重的边来构建生成树。

Kruskal算法的工作原理:
  1. 排序边:将图中的所有边按权重从小到大排序。
  2. 并查集:使用并查集结构来检测是否形成环路。
  3. 选择边:从小到大选择边,若加入该边不形成环路,则将其加入生成树。
  4. 重复:重复上述步骤,直到生成树包含V-1条边(V为顶点数)。
Java代码示例:
import java.util.*;public class KruskalMST {private List<Edge> mst = new ArrayList<>();public KruskalMST(Graph G) {PriorityQueue<Edge> pq = new PriorityQueue<>(Comparator.comparingInt(e -> e.weight));for (List<Edge> edges : G.adjList) {pq.addAll(edges);}UnionFind uf = new UnionFind(G.size());while (!pq.isEmpty() && mst.size() < G.size() - 1) {Edge e = pq.poll();int v = e.from;int w = e.to;if (!uf.isConnected(v, w)) {uf.union(v, w);mst.add(e);}}}public Iterable<Edge> edges() {return mst;}public double weight() {double weight = 0.0;for (Edge e : mst) {weight += e.weight;}return weight;}public static void main(String[] args) {Graph graph = new Graph(5);graph.addEdge(0, 1, 10);graph.addEdge(0, 3, 30);graph.addEdge(0, 4, 100);graph.addEdge(1, 2, 50);graph.addEdge(2, 4, 10);graph.addEdge(3, 2, 20);graph.addEdge(3, 4, 60);KruskalMST mst = new KruskalMST(graph);System.out.println("Edges in MST:");for (Edge e : mst.edges()) {System.out.println(e);}System.out.println("Total weight of MST: " + mst.weight());}
}class UnionFind {private int[] parent;public UnionFind(int n) {parent = new int[n];for (int i = 0; i < n; i++) {parent[i] = -1;}}public void validate(int v1) {if (v1 < 0 || v1 >= parent.length) {throw new IndexOutOfBoundsException("Index " + v1 + " is not valid.");}}public int sizeOf(int v1) {validate(v1);int root = find(v1);return -parent[root];}public int parent(int v1) {validate(v1);return parent[v1];}public boolean isConnected(int v1, int v2) {validate(v1);validate(v2);return find(v1) == find(v2);}public void union(int v1, int v2) {validate(v1);validate(v2);int root1 = find(v1);int root2 = find(v2);if (root1 == root2) {return;}int size1 = -parent[root1];int size2 = -parent[root2];if (size1 <= size2) {parent[root1] = root2;parent[root2] = -(size1 + size2);} else {parent[root2] = root1;parent[root1] = -(size1 + size2);}}public int find(int v1) {validate(v1);if (parent[v1] < 0) {return v1;}parent[v1] = find(parent[v1]);return parent[v1];}
}
Graph类的定义

为了完整性,我们需要定义一个Graph类,以支持上述算法的实现。

import java.util.*;class Graph {final List<List<Edge>> adjList;public Graph(int vertices) {adjList = new ArrayList<>(vertices);for (int i = 0; i < vertices; i++) {adjList.add(new ArrayList<>());}}public void addEdge(int from, int to, int weight) {adjList.get(from).add(new Edge(from, to, weight));adjList.get(to).add(new Edge(to, from, weight)); // For undirected graph}public List<Edge> getEdges(int vertex) {return adjList.get(vertex);}public List<Edge> edges() {List<Edge> edges = new ArrayList<>();for (List<Edge> edgesList : adjList) {edges.addAll(edgesList);}return edges;}public int size() {return adjList.size();}static class Edge {final int from;final int to;final int weight;Edge(int from, int to, int weight) {this.from = from;this.to = to;this.weight = weight;}@Overridepublic String toString() {return String.format("%d - %d: %d", from, to, weight);}}
}

对比分析

解决的问题
  • Dijkstra算法:解决单源最短路径问题。
  • Prim算法:解决最小生成树问题,适用于稠密图。
  • Kruskal算法:解决最小生成树问题,适用于稀疏图。
优劣性
  • Dijkstra算法
    • 优点:适用于有权重且无负权边的图。
    • 缺点:无法处理负权边。
  • Prim算法
    • 优点:适用于稠密图。
    • 缺点:在稀疏图中效率较低。
  • Kruskal算法
    • 优点:适用于稀疏图,简单易实现。
    • 缺点:在处理稠密图时效率较低。
异同点
  • 相同点
    • Prim和Kruskal算法都用于解决最小生成树问题。
    • 都使用贪心算法思想。
    • Dijkstra和Prim算法都使用优先队列来获取下一个应该加入的节点
  • 不同点
    • Dijkstra算法用于最短路径问题,Prim和Kruskal算法用于最小生成树问题。
    • Prim算法从顶点开始构建生成树,Kruskal算法从边开始构建生成树。
    • Dijkstra算法在Relax函数中是与源节点距离作比较,Prim算法在scan函数中是与当前在生成树中的节点距离做比较。

总结

通过对Dijkstra、Prim和Kruskal算法的介绍和代码实现,我们可以更好地理解这三种经典算法在图论中的应用。每种算法都有其适用场景和优劣性,选择合适的算法可以提高解决问题的效率。希望本文能为你在学习和应用图算法时提供帮助。

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

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

相关文章

为什么电容两端电压不能突变

我们先从RC延时电路说起吧&#xff0c;图1是最简单的RC延时电路&#xff0c;给一个阶跃的电压信号&#xff0c;电压会变成黄色曲线这个样子&#xff0c;这是为什么呢&#xff1f; 图1 电压跳变后&#xff0c;电源负极电子移动到电容下极板&#xff0c;排斥上极板电子流动到电源…

c++实现二叉搜索树(上)

宝贝们&#xff0c;好久不见&#xff0c;甚是想念&#x1f917;小吉断更了差多有10多天&#xff0c;在断更的日子里&#xff0c;小吉也有在好好学习数据结构与算法&#xff0c;但是学的并不多而且学的并不是很认真。主要是中途笔记本屏出现问题了&#xff08;这件事有点让小吉我…

Neo4j连接

终端输入&#xff1a; neo4j console 浏览器访问&#xff1a;http://localhost:7474/ 输入用户名和密码&#xff1a;neo4j&#xff0c; 梦想密码&#xff08;首次neo4j&#xff09; 代码连接用新的服务器地址&#xff1a; g Graph(neo4j://localhost:7687, auth(neo4j, ))…

函数递归(C语言)(详细过程!)

函数递归 一. 递归是什么1.1 递归的思想1.2 递归的限制条件 二. 递归举例2.1 求n的阶乘2.2 按顺序打印一个整数的每一位 三. 递归与迭代3.1 求第n个斐波那契数 一. 递归是什么 递归是学习C语言很重要的一个知识&#xff0c;递归就是函数自己调用自己&#xff0c;是一种解决问题…

2024年中国移动游戏市场研究报告

来源&#xff1a;点点数据&#xff1a; 近期历史回顾&#xff1a; 面向水泥行业的5G虚拟专网技术要求&#xff08;2024&#xff09;.pdf 2024年F5G-A绿色万兆全光园区白皮书.pdf 2024年全球废物管理展望报告.pdf 内容管理系统 2024-2025中国羊奶粉市场消费趋势洞察报告.pdf 20…

Web端在线Stomp服务测试与WebSocket服务测试

Stomp服务测试 支持连接、发送、订阅、接收&#xff0c;可设置请求头、自动重连 低配置云服务器&#xff0c;首次加载速度较慢&#xff0c;请耐心等候 预览页面&#xff1a;http://www.daelui.com/#/tigerlair/saas/preview/lxbho9lkzvgc 演练页面&#xff1a;http://www.da…

Django之云存储(一)

一、介绍 用户上传的文件以及项目中使用的静态文件,除了保存在本地服务器,还在可以保存在云服务中,比如: 阿里云七牛云(课程选用)亚马逊云等1.1、使用方式 注册账号 七牛云开发者平台 实名认证 创建空间

pycharm终端pip安装模块成功但还是显示找不到 ModuleNotFoundError: No module named

报错信息&#xff1a; ModuleNotFoundError: No module named 但是分明已经安装过此模块&#xff1a; 在cmd运行pip list 查看所有安装过的包找到了安装过&#xff1a; 如果重新安装就是这样&#xff1a;显示已经存在了 问题排查&#xff1a; 直接根据重新安装的显示已存在的…

Beego 使用教程 9:ORM 操作数据库(上)

beego 是一个用于Go编程语言的开源、高性能的 web 框架 beego 被用于在Go语言中企业应用程序的快速开发&#xff0c;包括RESTful API、web应用程序和后端服务。它的灵感来源于Tornado&#xff0c; Sinatra 和 Flask beego 官网&#xff1a;http://beego.gocn.vip/ 上面的 be…

HCIP认证笔记(判断题)

192、两台LSR之间交换hello消息&#xff0c;触发LDP会话建立&#xff0c;hello消息会携带传输地址&#xff0c;传输地址大的一方将作为主动方&#xff1b; 193、两台LSR发送hello消息&#xff0c;其中hello消息会采用组播或单播的形式发送&#xff1b; 194、LSR ID&#xff1a;…

pikachu靶场通关全流程

目录 暴力破解&#xff1a; 1.基于表单的暴力破解&#xff1a; 2.验证码绕过(on server)&#xff1a; 3.验证码绕过(on client)&#xff1a; token防爆破&#xff1a; XSS&#xff1a; 1.反射型xss(get)&#xff1a; 2.反射性xss(post)&#xff1a; 3.存储型xss&#…

2024中国网络安全产品用户调查报告(发布版)

自2020年始&#xff0c;人类进入了21世纪的第二个十年&#xff0c;全球进入了百年未有之大变局&#xff0c;新十年的开始即被新冠疫情逆转了全球化发展的历程&#xff0c;而至2022年3月俄乌战争又突然爆发&#xff0c;紧接着2023年7月“巴以冲突"皱起&#xff0c;世界快速…

7.Nginx动静分离

介绍 把动态和静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。 动静分离从目前实现角度分为两种: 1.纯粹把静态文件独立成单独的域名,放在独立的静态资源服务器上,目前主流推崇的方案。 2.动态和静态文件混合在一起发布,通过nginx来分开。 通过loc…

Flutter基础 -- Flutter常用组件

目录 1. 文本组件 Text 1.1 基础用法 1.2 Text 定义 1.3 Text 示例 1.4 Text.rich、RichText 、TextSpan 1.5 RichText 示例 2. 导入资源 2.1 加入资源 2.2 加入图片 3. 图片组件 image 3.1 colorBlendMode 混合参数 3.2 fit 图片大小适配 3.3 ImageProvider 图片…

申请资质的企业如何证明其团队的专业性?

教育背景与专业资格&#xff1a; 提供团队成员的学历证明和专业资格证书&#xff0c;例如注册建筑师、注册结构工程师或其他相关专业领域的注册资格。这些证书应由权威机构颁发&#xff0c;证明个人的专业知识和技能水平。 工作经验&#xff1a; 展示团队成员的相关工作经验&a…

Java中的安全管理器和权限控制

Java安全管理器&#xff08;Security Manager&#xff09;是Java安全模型的重要组成部分&#xff0c;它提供了一种机制&#xff0c;通过检查和控制应用程序代码的行为来保护系统资源不被恶意代码滥用。本文将详细介绍Java安全管理器的基本概念、使用方法及其在实际项目中的应用…

Echarts 绘制自定义图形

文章目录 需求分析需求 在 Echarts 中绘制一个不规则图形放置在指定位置 分析 可以先从一个六边形画起 可以使用 ECharts 中的『自定义图形』来绘制一个封闭的六边形。以下是一个简单的示例代码,演示了如何使用 ECharts 绘制一个封闭的六边形: <!DOCTYPE html> &l…

《数据结构与算法之美》学习笔记二

前言&#xff1a;本篇文章介绍了一下二叉树中的基本知识点&#xff0c;包括二叉树的种类、二叉树的存储方式以及二叉树的深度和广度优先遍历&#xff1b;以及《数据结构与算法》中对于数组的讲解记录&#xff0c;只记录了本前端能看懂的&#x1f913;&#xff0c;还有很多知识点…

视频行人搜索 (Person Search in Videos)

文章目录 视频行人搜索 (Person Search in Videos)图像行人搜索存在问题Video PS 定义MTA-PS数据集First person search dataset in videosComplicated ambient conditions and realistic monitoring scenariosPrivacy insensitivity 方法 视频行人搜索 (Person Search in Vide…

FiRa标准UWB MAC实现(三)——距离如何获得?

继续前期FiRa MAC相关介绍,将FiRa UWB MAC层相关细节进一步进行剖析,介绍了UWB技术中最重要的一个点,高精度的距离是怎么获得的,具体使用的测距方法都有哪些,原理又是什么。为后续FiRa UWB MAC的实现进行铺垫。 3、测距方法 3.1 SS-TWR SS-TWR为Single-Sided Two-Way Ra…