探索图论中的关键算法(Java 实现)

news2024/9/17 9:27:57

“日出东海落西山 愁也一天 喜也一天 遇事不钻牛角尖”

文章目录

  • 前言
    • 文章有误敬请斧正 不胜感恩!||Day03
    • 1. 最短路径算法
      • Dijkstra算法
        • Java 实现:
      • Bellman-Ford算法
        • Java 实现:
    • 2. 最小生成树算法
      • Prim算法
        • Java 实现:
      • Kruskal算法
        • Java 实现:
    • 3. 二分图的最大匹配
      • 匈牙利算法
        • Java 实现:
    • 4. 网络流
      • Dinic算法
        • Java 实现:
  • 总结


前言

今天讲什么?:

图论是计算机科学中一个非常重要的分支,在困难题中,很多时候图论算法都能提供有效的解决方案。
通过学习这些经典的图论算法,我们可以更好地理解有关:
如何用最优的方式在节点和边之间进行数据传递的算法问题。

本文将带你初步探索图论中的几种核心算法,如最短路径算法、最小生成树、二分图匹配、网络流等,并通过Java语言为这些算法提供通俗易懂的实现和讲解。即使你对图论的理解有限,本文也会帮助你轻松掌握这些关键的算法和它们的实际应用场景。并给出我常用的模板供大家参考.


文章有误敬请斧正 不胜感恩!||Day03

提示:以下是本篇文章正文内容,


在这里插入图片描述


图论是计算机科学中的一个重要领域,涉及节点(顶点)和边的相关问题。本文深入探讨了几个关键的图论算法,包括最短路径算法、最小生成树、图匹配算法以及网络流算法,所有的实现都使用Java语言编写。

1. 最短路径算法

最短路径算法用于在加权图中寻找从一个节点到其他节点的最短路径。它们被广泛应用于GPS系统、网络路由等场景。

Dijkstra算法

Dijkstra算法用于找到带有非负权重的图中从源节点到其他节点的最短路径。该算法使用优先队列来扩展当前已知的最短距离。

Java 实现:
import java.util.*;

class Dijkstra {
    static final int INF = Integer.MAX_VALUE;
    static List<List<int[]>> adj = new ArrayList<>();
    static int[] dist;
    static boolean[] visited;

    static void dijkstra(int start, int n) {
        dist = new int[n];
        visited = new boolean[n];
        Arrays.fill(dist, INF);
        
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        dist[start] = 0;
        pq.add(new int[]{0, start});

        while (!pq.isEmpty()) {
            int[] curr = pq.poll();
            int u = curr[1];

            if (visited[u]) continue;
            visited[u] = true;

            for (int[] edge : adj.get(u)) {
                int v = edge[0], weight = edge[1];
                if (dist[v] > dist[u] + weight) {
                    dist[v] = dist[u] + weight;
                    pq.add(new int[]{dist[v], v});
                }
            }
        }
    }

    public static void main(String[] args) {
        // 初始化图
        int n = 5;
        for (int i = 0; i < n; i++) adj.add(new ArrayList<>());
        // 添加边 (节点, 权重)
        adj.get(0).add(new int[]{1, 2});
        adj.get(1).add(new int[]{2, 1});
        adj.get(0).add(new int[]{2, 4});

        dijkstra(0, n);
        System.out.println(Arrays.toString(dist));
    }
}

Bellman-Ford算法

Bellman-Ford算法可以处理包含负权重的图,它通过多次松弛所有的边来寻找最短路径,并检测是否存在负权重环。

Java 实现:
import java.util.Arrays;

class BellmanFord {
    static class Edge {
        int u, v, w;
        Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }
    }

    static final int INF = Integer.MAX_VALUE;
    static Edge[] edges;
    static int[] dist;
    static int n, m;

    static void bellmanFord(int start) {
        dist = new int[n];
        Arrays.fill(dist, INF);
        dist[start] = 0;

        for (int i = 0; i < n - 1; i++) {
            for (Edge e : edges) {
                if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v]) {
                    dist[e.v] = dist[e.u] + e.w;
                }
            }
        }
    }

    public static void main(String[] args) {
        n = 5; m = 5;
        edges = new Edge[]{
                new Edge(0, 1, -1),
                new Edge(0, 2, 4),
                new Edge(1, 2, 3),
                new Edge(1, 3, 2),
                new Edge(1, 4, 2)
        };

        bellmanFord(0);
        System.out.println(Arrays.toString(dist));
    }
}

2. 最小生成树算法

最小生成树(MST)是涵盖图中所有顶点且具有最小边权重和的树。它们广泛应用于网络设计问题,如道路、通信网络的构建等。

Prim算法

Prim算法通过逐步扩展当前树的最小边来构建MST,适用于密集图。

Java 实现:
import java.util.Arrays;

class Prim {
    static final int INF = Integer.MAX_VALUE;
    static int[][] graph;
    static boolean[] visited;
    static int[] dist;

    static int prim(int n) {
        dist = new int[n];
        visited = new boolean[n];
        Arrays.fill(dist, INF);
        dist[0] = 0;
        int res = 0;

        for (int i = 0; i < n; i++) {
            int t = -1;
            for (int j = 0; j < n; j++) {
                if (!visited[j] && (t == -1 || dist[j] < dist[t])) t = j;
            }

            if (dist[t] == INF) return -1;
            visited[t] = true;
            res += dist[t];

            for (int j = 0; j < n; j++) {
                dist[j] = Math.min(dist[j], graph[t][j]);
            }
        }
        return res;
    }

    public static void main(String[] args) {
        int n = 5;
        graph = new int[n][n];
        for (int[] row : graph) Arrays.fill(row, INF);
        graph[0][1] = 2;
        graph[1][2] = 3;
        graph[0][2] = 1;

        System.out.println(prim(n));
    }
}

Kruskal算法

Kruskal算法是一种贪心算法,通过排序所有边并不断选择最小边来构建MST,确保不会形成环。

Java 实现:
import java.util.*;

class Kruskal {
    static class Edge implements Comparable<Edge> {
        int u, v, weight;
        Edge(int u, int v, int weight) {
            this.u = u;
            this.v = v;
            this.weight = weight;
        }

        public int compareTo(Edge other) {
            return Integer.compare(this.weight, other.weight);
        }
    }

    static int[] parent;

    static int find(int x) {
        if (parent[x] != x) parent[x] = find(parent[x]);
        return parent[x];
    }

    static void union(int u, int v) {
        parent[find(u)] = find(v);
    }

    static int kruskal(List<Edge> edges, int n) {
        Collections.sort(edges);
        parent = new int[n];
        for (int i = 0; i < n; i++) parent[i] = i;

        int mstWeight = 0;
        for (Edge e : edges) {
            if (find(e.u) != find(e.v)) {
                mstWeight += e.weight;
                union(e.u, e.v);
            }
        }
        return mstWeight;
    }

    public static void main(String[] args) {
        List<Edge> edges = new ArrayList<>();
        edges.add(new Edge(0, 1, 10));
        edges.add(new Edge(1, 2, 6));
        edges.add(new Edge(0, 2, 5));

        System.out.println(kruskal(edges, 3));
    }
}

3. 二分图的最大匹配

最大匹配问题涉及将二分图中两个不相交的集合顶点进行配对,以使配对数最大。

匈牙利算法

匈牙利算法用于求解二分图中的最大匹配问题。

Java 实现:
import java.util.*;

class Hungarian {
    static List<List<Integer>> adj = new ArrayList<>();
    static int[] match;
    static boolean[] visited;
    static int n;

    static boolean dfs(int u) {
        for (int v : adj.get(u)) {
            if (!visited[v]) {
                visited[v] = true;
                if (match[v] == -1 || dfs(match[v])) {
                    match[v] = u;
                    return true;
                }
            }
        }
        return false;
    }

    static int hungarian() {
        match = new int[n];
        Arrays.fill(match, -1);
        int result = 0;

        for (int i = 0; i < n; i++) {
            visited = new boolean[n];
            if (dfs(i)) result++;
        }
        return result;
    }

    public static void main(String[] args) {
        n = 5;
        for (int i = 0; i < n; i++) adj.add(new ArrayList<>());
        adj.get(0).add(1);
        adj.get(1).add(2);

        System.out.println(hungarian());
    }
}

4. 网络流

网络流算法解决的是关于网络中的传输、循环和流量分配问题。

Dinic算法

Dinic算法通过构建分层图和阻塞流来高效地求解最大流问题。

Java 实现:
import java.util.*;



class Dinic {
    static class Edge {
        int v, flow, capacity;
        Edge rev;
        Edge(int v, int capacity) {
            this.v = v;
            this.capacity = capacity;
            this.flow = 0;
        }
    }

    static List<List<Edge>> adj = new ArrayList<>();
    static int[] level;
    static int n;

    static void addEdge(int u, int v, int capacity) {
        Edge uv = new Edge(v, capacity);
        Edge vu = new Edge(u, 0);
        uv.rev = vu;
        vu.rev = uv;
        adj.get(u).add(uv);
        adj.get(v).add(vu);
    }

    static boolean bfs(int s, int t) {
        Arrays.fill(level, -1);
        Queue<Integer> q = new LinkedList<>();
        level[s] = 0;
        q.add(s);

        while (!q.isEmpty()) {
            int u = q.poll();
            for (Edge e : adj.get(u)) {
                if (level[e.v] == -1 && e.flow < e.capacity) {
                    level[e.v] = level[u] + 1;
                    q.add(e.v);
                }
            }
        }
        return level[t] != -1;
    }

    static int dfs(int u, int t, int flow) {
        if (u == t) return flow;
        int totalFlow = 0;

        for (Edge e : adj.get(u)) {
            if (level[e.v] == level[u] + 1 && e.flow < e.capacity) {
                int currFlow = dfs(e.v, t, Math.min(flow, e.capacity - e.flow));
                if (currFlow > 0) {
                    e.flow += currFlow;
                    e.rev.flow -= currFlow;
                    totalFlow += currFlow;
                    flow -= currFlow;
                    if (flow == 0) break;
                }
            }
        }
        return totalFlow;
    }

    static int dinic(int s, int t) {
        int maxFlow = 0;
        level = new int[n];
        while (bfs(s, t)) {
            maxFlow += dfs(s, t, Integer.MAX_VALUE);
        }
        return maxFlow;
    }

    public static void main(String[] args) {
        n = 6;
        for (int i = 0; i < n; i++) adj.add(new ArrayList<>());
        addEdge(0, 1, 16);
        addEdge(0, 2, 13);
        addEdge(1, 2, 10);
        addEdge(2, 3, 14);
        addEdge(3, 4, 7);

        System.out.println(dinic(0, 4));
    }
}

总结

在这里插入图片描述


通过本文的学习,我们深入探讨了图论中的几个核心算法,并且用Java进行了详细的实现。这些算法不仅仅是理论上的工具,它们在实际问题中有着广泛的应用:

  • 最短路径算法 帮助我们在各种网络中找到最有效的传输路径,比如导航系统或通信网络。
  • 最小生成树算法 能帮助我们以最低成本构建网络,广泛应用于电网、道路建设等。
  • 二分图匹配算法 则在资源分配、任务调度等问题中提供了最佳的解决方案。
  • 网络流算法 通过优化资源流动,解决了物流、流量控制等领域中的实际问题。

掌握这些经典算法,
记住这些模板
不仅能提升帮我们打比赛,还可以为我们在实际开发中提供强大的工具。
希望通过本文的学习,图论算法我们已经初步了解了
多做题,并不断应用才能更好的掌握.

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

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

相关文章

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态&#xff0c;生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案&#xff0c;则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时&#x…

读软件设计的要素05概念的特性

1. 概念的特性 1.1. 专一性原则(specificity principle)认为概念与目的应该一一对应 1.1.1. 专一性原则已被证明是概念设计中最有用的原则之一 1.1.2. 一个概念最多只能满足一个目的 1.2. 很少有没有目的的概念 1.2.1. 如果本应隐藏的用户机制被暴露&#xff0c;可能会产生…

通信工程学习:什么是2ASK/BASK二进制振幅键控

2ASK/BASK&#xff1a;二进制振幅键控 2ASK/BASK二进制振幅键控是一种数字调制技术&#xff0c;其全称是二进制振幅键控&#xff08;Binary Amplitude Shift Keying&#xff09;。该技术通过改变载波的振幅来传递二进制数字信息&#xff0c;而载波的频率和相位则保持不变。以下…

RISC-V (九)抢占式多任务

主要的思想&#xff1a;借用定时器中断实现。设置定时器寄存器&#xff0c;系统自动触发定时器中断时会跳到trap handler这个函数里。借用这个函数做上下文的切换&#xff0c;从而实现了抢占式多任务。 定时器中断&#xff1a;跳到trap handler函数&#xff0c;同时系统自动将…

清华计算几何--凸Polygon的相交问题

凸Polygon和相交定义 本节只讨论凸Polygon的问题&#xff0c;不涉及凹Polygon. 相交包含了边相交和完全包含。 凸Polygon相交的两个问题 Detection(检测) 判断两个凸Polygon是否相交&#xff0c;至于相交部分是什么不关心. Construction(构造) 求出两个凸Polygon具体相交…

Linux_kernel移植rootfs10

一、动态更改内核 1、low level&#xff08;静态修改&#xff09; 【1】将led_drv.c拷贝到kernel/drivers/char/目录中 【2】修改当前目录下的Makefile文件 obj-y led_drv.o #将新添加的驱动文件加入到Makefile文件中 【3】退回kernel目录&#xff0c;执行make uImage …

熬夜后补救措施

人体的肝功能问题 直接体现在体态和容颜上 伤肝 三大坏行为 熬夜后补救 *补充养b族、口、、锌、硒 加强代谢 能力 (1)另外熬夜后一定要多喝水 提升身体代谢能力 (2)谷肤甘肽清肝 肝脏排毒&#xff0c;减轻负拒 (3)水飞前含量高点 &#xff08;4)熬夜出更多油 容易长痘 需要清…

标准库标头 <filesystem> (C++17)学习之文件类型

本篇介绍filesystem文件库的文件类型API。 文件类型 is_block_file (C17) 检查给定的路径是否表示块设备 (函数) is_character_file (C17) 检查给定的路径是否表示字符设备 (函数) is_directory (C17) 检查给定的路径是否表示一个目录 (函数) is_empty (C17) 检查给定的路径是…

STM32G474之使用DAC1和DAC2测试模拟比较器

STM32G474使用DAC1和DAC2的输出作为比较器输入&#xff0c;测试模拟比较器&#xff0c;方法如下&#xff1a; PA1的附加功能为COMP1_INP&#xff0c;无需映射&#xff0c;直接将它配置为模拟功能&#xff0c;就可以使用了。 将COMP1_OUT引脚映射到PA0; 采用DAC2_OUT1输出电压给…

【大疆 SDR 图传 P1 】 功能拆解,通信功能剖析

大疆 SDR 图传 P1 拆解视频P1 SoC1、哲酷2、小米3、大疆&#xff08;文章主角&#xff09; 一、为什么说SDR技术1、sdr 软件无线电2、影视博主的测评方法3、第一个说自己SDR的还是这个老登 二、大疆的图传发展历程1、FPGA AD93632、 P1 自研1、2个DSP和一个CPU A72、音频子系统…

SpringMVC;MVC模式;Spring环境搭建;

一&#xff0c;介绍MVC模式&#xff1a; MVC模式&#xff1a; 1.M:model 模型,业务模型和数据模型. 2.C:controller 控制器 3.V:view 视图 优点: 使用控制器C把视图V和业务模型M分离&#xff0c;从而使同一个程序可以使用不同的表现形式 使用场景: 中大型项目 核心: 控制器 二…

828华为云征文 | 基于Docker与Jenkins实现自动化部署

需要了解 本文章主要讲述在 华为云Flexus X 实例上使用docker快速部署持续集成工具 Jenkins&#xff0c;通过插件来自动化CI/CD过程中的各种琐碎功能。选择合适的云服务器&#xff1a; 本文采用的是 华为云服务器 Flexus X 实例&#xff08;推荐使用&#xff09;连接方式&#…

【自动驾驶】决策规划算法 | 数学基础(三)直角坐标与自然坐标转换Ⅰ

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

【有啥问啥】数字孪生(Digital Twin)技术在人工智能中的应用

数字孪生技术在人工智能中的应用 在当今的数字化转型过程中&#xff0c;“数字孪生”技术逐渐成为热门话题&#xff0c;并且在各个行业中展现出巨大的潜力。作为一种新兴技术&#xff0c;数字孪生&#xff08;Digital Twin&#xff09;不仅仅是物理对象的虚拟复制品&#xff0…

MATLAB算法实战应用案例精讲-【人工智能】大数据审计(概念篇)

目录 前言 大数据审计发展历程 1.初级阶段:验证型逻辑占据主导地位 2.发展阶段:挖掘型逻辑突出重围 3.成熟阶段:基于验证和挖掘的预测型逻辑发展 算法原理 什么是大数据审计 特征 事项审计 大数据审计的方法 (一)大数据审计的一般思路 (二)大数据审计的关键技术…

【开发工具】探索IntelliJ IDEA插件——JSON Parser,让JSON处理变得轻松高效

开发过程中&#xff0c;遇到一个字符串&#xff0c;需要判断是否是JSON格式&#xff0c;或者是需要将Json字符串美化展示&#xff0c;是否还在打开百度搜JSON在线格式化(https://www.bejson.com/)&#xff0c;是否还在写个main方法将字符串转成JSON格式并输出。这篇文章&#x…

【Linux】全面讲解 Shell 变量的那些事

本文内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01; 如果对您有帮助&#xff0c;烦请点赞、关注、转发、订阅专栏&#xff01; 专栏订阅入口 Linux 专栏 | Docker 专栏 | Kubernetes 专栏 往期精彩文章 【Docker】&#xff08;全网首发&#xff09;Kyl…

python中的循环结构

注意&#xff1a;range&#xff08;&#xff09;函数 累加和&#xff1a; 注意&#xff1a;if 下面如果有好几行&#xff0c;只执行一行 print必须和 for 开头相同格数 例题&#xff1a;水仙花数 注意在print语句中&#xff0c;一句好“ 。。。。。 ”后面必须有逗号然后再写变…

(八) 初入MySQL 【主从复制】

案例概况 在企业应用中&#xff0c;成熟的业务通常数据量都比较大 单台MySQL在安全性、 高可用性和高并发方面都无法满足实际的需求 &#xff0c;所以需要配置多台主从数据库服务器以实现读写分离来满足需求 一、主从复制原理 1.1、 MySQL的复制类型 基于语句的复制(STATEME…

C++11 的继续学习

1.lambda 我们如果想要给一个自定义的元素排序&#xff0c;那么应该怎么排呢 先举个例子&#xff1a; struct Goods {string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price),…