"日出东海落西山 愁也一天 喜也一天 遇事不钻牛角尖"
文章目录
- 前言
-
- [文章有误敬请斧正 不胜感恩!||Day03](#文章有误敬请斧正 不胜感恩!||Day03)
- [1. 最短路径算法](#1. 最短路径算法)
-
- Dijkstra算法
-
- [Java 实现:](#Java 实现:)
- Bellman-Ford算法
-
- [Java 实现:](#Java 实现:)
- [2. 最小生成树算法](#2. 最小生成树算法)
- [3. 二分图的最大匹配](#3. 二分图的最大匹配)
-
- 匈牙利算法
-
- [Java 实现:](#Java 实现:)
- [4. 网络流](#4. 网络流)
-
- Dinic算法
-
- [Java 实现:](#Java 实现:)
- 总结
前言
今天讲什么?:
图论是计算机科学中一个非常重要的分支,在困难题中,很多时候图论算法都能提供有效的解决方案。
通过学习这些经典的图论算法,我们可以更好地理解有关:
如何用最优的方式在节点和边之间进行数据传递的算法问题。
本文将带你初步探索图论中的几种核心算法,如最短路径算法、最小生成树、二分图匹配、网络流等,并通过Java语言为这些算法提供通俗易懂的实现和讲解。即使你对图论的理解有限,本文也会帮助你轻松掌握这些关键的算法和它们的实际应用场景。并给出我常用的模板供大家参考.
文章有误敬请斧正 不胜感恩!||Day03
提示:以下是本篇文章正文内容,
图论是计算机科学中的一个重要领域,涉及节点(顶点)和边的相关问题。本文深入探讨了几个关键的图论算法,包括最短路径算法、最小生成树、图匹配算法以及网络流算法,所有的实现都使用Java语言编写。
1. 最短路径算法
最短路径算法用于在加权图中寻找从一个节点到其他节点的最短路径。它们被广泛应用于GPS系统、网络路由等场景。
Dijkstra算法
Dijkstra算法用于找到带有非负权重的图中从源节点到其他节点的最短路径。该算法使用优先队列来扩展当前已知的最短距离。
Java 实现:
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 实现:
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 实现:
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 实现:
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 实现:
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 实现:
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进行了详细的实现。这些算法不仅仅是理论上的工具,它们在实际问题中有着广泛的应用:
- 最短路径算法 帮助我们在各种网络中找到最有效的传输路径,比如导航系统或通信网络。
- 最小生成树算法 能帮助我们以最低成本构建网络,广泛应用于电网、道路建设等。
- 二分图匹配算法 则在资源分配、任务调度等问题中提供了最佳的解决方案。
- 网络流算法 通过优化资源流动,解决了物流、流量控制等领域中的实际问题。
掌握这些经典算法,
记住这些模板
不仅能提升帮我们打比赛,还可以为我们在实际开发中提供强大的工具。
希望通过本文的学习,图论算法我们已经初步了解了
多做题,并不断应用才能更好的掌握.