Java图论算法竞赛应用详解
一、图的基本概念
1.1 什么是图
图(Graph)是一种非线性数据结构,由顶点(Vertex)的集合和边(Edge)的集合组成。在计算机科学中,图被广泛应用于表示各种实际问题,如社交网络、地图导航、网络拓扑等。
1.2 图的基本术语
- 顶点(Vertex):图中的节点
- 边(Edge):连接两个顶点的线段
- 有向图:边有方向
- 无向图:边无方向
- 权重:边上的数值
- 度:与顶点相连的边的数量
1.3 图的存储方式
1.3.1 邻接矩阵
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | public class AdjacencyMatrix {private int V;
 private int[][] matrix;
 
 public AdjacencyMatrix(int v) {
 V = v;
 matrix = new int[v][v];
 }
 
 
 public void addEdge(int source, int dest) {
 matrix[source][dest] = 1;
 matrix[dest][source] = 1;
 }
 }
 
 | 
1.3.2 邻接表
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | public class AdjacencyList {private int V;
 private ArrayList<ArrayList<Integer>> adj;
 
 public AdjacencyList(int v) {
 V = v;
 adj = new ArrayList<>(v);
 for (int i = 0; i < v; i++) {
 adj.add(new ArrayList<>());
 }
 }
 
 public void addEdge(int source, int dest) {
 adj.get(source).add(dest);
 adj.get(dest).add(source);
 }
 }
 
 | 
二、图的基本算法
2.1 深度优先搜索(DFS)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | public class DFS {private boolean[] visited;
 private ArrayList<ArrayList<Integer>> adj;
 
 public DFS(ArrayList<ArrayList<Integer>> adj) {
 this.adj = adj;
 visited = new boolean[adj.size()];
 }
 
 public void dfs(int v) {
 visited[v] = true;
 System.out.print(v + " ");
 
 for (int n : adj.get(v)) {
 if (!visited[n]) {
 dfs(n);
 }
 }
 }
 }
 
 | 
2.2 广度优先搜索(BFS)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | public class BFS {private boolean[] visited;
 private ArrayList<ArrayList<Integer>> adj;
 
 public BFS(ArrayList<ArrayList<Integer>> adj) {
 this.adj = adj;
 visited = new boolean[adj.size()];
 }
 
 public void bfs(int start) {
 Queue<Integer> queue = new LinkedList<>();
 visited[start] = true;
 queue.add(start);
 
 while (!queue.isEmpty()) {
 int v = queue.poll();
 System.out.print(v + " ");
 
 for (int n : adj.get(v)) {
 if (!visited[n]) {
 visited[n] = true;
 queue.add(n);
 }
 }
 }
 }
 }
 
 | 
2.3 最短路径算法
2.3.1 Dijkstra算法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | public class Dijkstra {private static final int INF = Integer.MAX_VALUE;
 
 public static int[] shortestPath(int[][] graph, int src) {
 int V = graph.length;
 int[] dist = new int[V];
 boolean[] visited = new boolean[V];
 
 Arrays.fill(dist, INF);
 dist[src] = 0;
 
 for (int count = 0; count < V-1; count++) {
 int u = minDistance(dist, visited);
 visited[u] = true;
 
 for (int v = 0; v < V; v++) {
 if (!visited[v] && graph[u][v] != 0 &&
 dist[u] != INF &&
 dist[u] + graph[u][v] < dist[v]) {
 dist[v] = dist[u] + graph[u][v];
 }
 }
 }
 return dist;
 }
 
 private static int minDistance(int[] dist, boolean[] visited) {
 int min = INF, minIndex = -1;
 for (int v = 0; v < dist.length; v++) {
 if (!visited[v] && dist[v] <= min) {
 min = dist[v];
 minIndex = v;
 }
 }
 return minIndex;
 }
 }
 
 | 
2.4 最小生成树
2.4.1 Kruskal算法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 
 | public class Kruskal {class Edge implements Comparable<Edge> {
 int src, dest, weight;
 
 public int compareTo(Edge other) {
 return this.weight - other.weight;
 }
 }
 
 class DisjointSet {
 int[] parent, rank;
 
 DisjointSet(int n) {
 parent = new int[n];
 rank = new int[n];
 for (int i = 0; i < n; i++) {
 parent[i] = i;
 }
 }
 
 int find(int x) {
 if (parent[x] != x) {
 parent[x] = find(parent[x]);
 }
 return parent[x];
 }
 
 void union(int x, int y) {
 int xRoot = find(x);
 int yRoot = find(y);
 
 if (rank[xRoot] < rank[yRoot]) {
 parent[xRoot] = yRoot;
 } else if (rank[xRoot] > rank[yRoot]) {
 parent[yRoot] = xRoot;
 } else {
 parent[yRoot] = xRoot;
 rank[xRoot]++;
 }
 }
 }
 
 public Edge[] kruskalMST(Edge[] edges, int V) {
 Arrays.sort(edges);
 Edge[] result = new Edge[V-1];
 DisjointSet ds = new DisjointSet(V);
 
 int e = 0, i = 0;
 while (e < V-1) {
 Edge nextEdge = edges[i++];
 int x = ds.find(nextEdge.src);
 int y = ds.find(nextEdge.dest);
 
 if (x != y) {
 result[e++] = nextEdge;
 ds.union(x, y);
 }
 }
 return result;
 }
 }
 
 | 
三、实际应用案例
3.1 图的连通性判断
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | public class ConnectivityCheck {private int V;
 private ArrayList<ArrayList<Integer>> adj;
 
 public ConnectivityCheck(int v) {
 V = v;
 adj = new ArrayList<>(V);
 for (int i = 0; i < V; i++) {
 adj.add(new ArrayList<>());
 }
 }
 
 public void addEdge(int v, int w) {
 adj.get(v).add(w);
 adj.get(w).add(v);
 }
 
 private void DFSUtil(int v, boolean[] visited) {
 visited[v] = true;
 for (int n : adj.get(v)) {
 if (!visited[n]) {
 DFSUtil(n, visited);
 }
 }
 }
 
 public boolean isConnected() {
 boolean[] visited = new boolean[V];
 DFSUtil(0, visited);
 
 for (boolean v : visited) {
 if (!v) return false;
 }
 return true;
 }
 }
 
 | 
3.2 拓扑排序
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | public class TopologicalSort {private int V;
 private ArrayList<ArrayList<Integer>> adj;
 
 public TopologicalSort(int v) {
 V = v;
 adj = new ArrayList<>(V);
 for (int i = 0; i < V; i++) {
 adj.add(new ArrayList<>());
 }
 }
 
 public void addEdge(int v, int w) {
 adj.get(v).add(w);
 }
 
 private void topologicalSortUtil(int v, boolean[] visited,
 Stack<Integer> stack) {
 visited[v] = true;
 
 for (int n : adj.get(v)) {
 if (!visited[n]) {
 topologicalSortUtil(n, visited, stack);
 }
 }
 
 stack.push(v);
 }
 
 public int[] topologicalSort() {
 Stack<Integer> stack = new Stack<>();
 boolean[] visited = new boolean[V];
 
 for (int i = 0; i < V; i++) {
 if (!visited[i]) {
 topologicalSortUtil(i, visited, stack);
 }
 }
 
 int[] result = new int[V];
 for (int i = 0; i < V; i++) {
 result[i] = stack.pop();
 }
 return result;
 }
 }
 
 | 
四、优化技巧与注意事项
4.1 数据结构选择
- 稀疏图(边较少):使用邻接表,空间复杂度O(V+E)
- 稠密图(边较多):使用邻接矩阵,空间复杂度O(V²)
- 需要快速判断两点是否相连:使用邻接矩阵
- 需要遍历某个顶点的所有邻接点:使用邻接表
4.2 时间复杂度分析
- DFS/BFS:O(V+E)
- Dijkstra:O(V²),使用优先队列可优化到O((V+E)logV)
- Kruskal:O(ElogE)
- 拓扑排序:O(V+E)
4.3 常见优化方法
- 使用优先队列优化Dijkstra算法
- 路径压缩优化并查集
- 使用BitSet代替boolean数组标记访问状态
- 预处理技术(如Floyd-Warshall算法)
4.4 竞赛中的注意事项
- 初始化:注意数组大小,避免越界
- 边界条件:处理特殊情况(如空图、单个节点)
- 数据范围:注意整型溢出
- 输入输出:使用BufferedReader/BufferedWriter加速IO
- 代码复用:封装常用操作为工具方法
五、总结
图论算法在竞赛中是一个重要的知识点,掌握好基本概念和常用算法的实现是关键。在实际应用中,需要根据具体问题选择合适的算法和数据结构,同时注意代码的效率和正确性。通过不断练习和总结,才能在竞赛中游刃有余地运用图论算法解决各种问题。