1.邻接表
static class Edge {int next;int value;public Edge(int next, int value) {this.next = next;this.value = value;}}static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();public static void addEgde(int from, int to, int value) {if (!graph.containsKey(from)) {graph.put(from, new LinkedList<>());}if (!graph.containsKey(to)) {graph.put(to, new LinkedList<>());}graph.get(from).add(new Edge(to, value));graph.get(to).add(new Edge(from, value));}
2.dijkstra
求单源最短路,不能求带有负权边
import java.util.*;
public class Main{static class Edge {int next;int value;public Edge(int next, int value) {this.next = next;this.value = value;}}static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();//用于记录起始点到每个点的最短距离static int[] distance;//用于记录每个点是否被标记过,如果被标记过说明这个点已经是最短路径了static boolean[] visited;static int N;static int M;public static void addEgde(int from, int to, int value) {if (!graph.containsKey(from)) {graph.put(from, new LinkedList<>());}if (!graph.containsKey(to)) {graph.put(to, new LinkedList<>());}graph.get(from).add(new Edge(to, value));graph.get(to).add(new Edge(from, value));}public static void dijkstra(int start) {Arrays.fill(distance, Integer.MAX_VALUE);Arrays.fill(visited, false);distance[start] = 0;int N = graph.size();for (int i = 0; i < N; i++) {//1.找到distance的最小值int min = Integer.MAX_VALUE;int min_index = -1;for (int j = 0; j < distance.length; j++) {if (!visited[j] && min > distance[j]) {min = distance[j];min_index = j;}}if (min_index == -1) {return;}//2.标记min_index=truevisited[min_index] = true;//3.更新从min_index出发的边LinkedList<Edge> edges = graph.get(min_index);for (int j = 0; j < edges.size(); j++) {int next = edges.get(j).next;int value = edges.get(j).value;if (!visited[next] && distance[min_index] + value < distance[next]) {distance[next] = distance[min_index] + value;}}}//输出结果System.out.println(Arrays.toString(distance));}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();int start = scanner.nextInt();for (int i = 0; i < 9; i++) {int from = scanner.nextInt();int to = scanner.nextInt();int value = scanner.nextInt();addEgde(from, to, value);}distance = new int[N + 1];visited = new boolean[N + 1];dijkstra(start);}
}
3.dijkstra优化版本
import java.util.*;
public class Main {static class Edge {int next;int value;public Edge(int next, int value) {this.next = next;this.value = value;}}static class Distance implements Comparable {int node;int dis;public Distance(int node, int dis) {this.node = node;this.dis = dis;}@Overridepublic int compareTo(Object o) {Distance object = (Distance) o;return Integer.compare(this.dis, object.dis);}}static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();static int N;static int M;static int[] distance;static boolean[] visited;static PriorityQueue<Distance> queue = new PriorityQueue<>();public static void addEdge(int from, int to, int value) {if (!graph.containsKey(from)) {graph.put(from, new LinkedList<>());}if (!graph.containsKey(to)) {graph.put(to, new LinkedList<>());}graph.get(from).add(new Edge(to, value));graph.get(to).add(new Edge(from, value));}public static void dijkstra(int start) {Arrays.fill(distance, Integer.MAX_VALUE);distance[start] = 0;//0.初始优先队列queue.add(new Distance(start, 0));while (!queue.isEmpty()) {//1.从优先队列中弹出优先级最高的,找到最小的distance,然后标记Distance dis = queue.poll();if (visited[dis.node]) continue;int min_index = dis.node;int min_dis = dis.dis;visited[min_index] = true;//2.更新从min发出的所有边LinkedList<Edge> edges = graph.get(min_index);for (Edge edge : edges) {int next = edge.next;int value = edge.value;if (!visited[next] && distance[next] > min_dis + value) {distance[next] = min_dis + value;//3.压入优先队列//虽然优先队列中会出现一个点的多个distance,但是后面的用不到queue.add(new Distance(next, distance[next]));}}}for (int i = 1; i < distance.length; i++) {System.out.print(distance[i] + " ");}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();int start = scanner.nextInt();for (int i = 0; i < M; i++) {int from = scanner.nextInt();int to = scanner.nextInt();int value = scanner.nextInt();addEdge(from, to, value);}distance = new int[N + 1];visited = new boolean[N + 1];dijkstra(start);}
}
4.Floyd
作用:可以用于求带负权边的多源最短路径,并且只能用于邻接矩阵。
Floyd是采用动态规划的思想实现的。
设dp[k][i][j]表示只用前 k 个点时,i 到 j 之间的最短路径是dp[k][i][j]
初始化:dp[0][i][j]表示点与点之间的距离
状态转移:dp[k][i][j] = min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]),注意k是最外层循环
然后进行空间压缩:dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j])
import java.util.*;
public class Main{static int N;static int M;static int[][] distance;public static void floyd() {for (int k = 1; k <= N; k++) {for (int i = 1; i <= N; i++) {for (int j = 1; j <= N; j++) {distance[i][j] = Math.min(distance[i][j], distance[i][k] + distance[k][j]);}}}for (int i = 1; i <= N; i++) {for (int j = 1; j <= N; j++) {System.out.print(distance[i][j] + " ");}System.out.println();}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();distance = new int[N + 1][N + 1];//初始化for (int i = 1; i < N + 1; i++) {Arrays.fill(distance[i], Integer.MAX_VALUE);}for (int i = 1; i <= N; i++) {distance[i][i] = 0;}//输入数据for (int i = 0; i < N; i++) {int from = scanner.nextInt();int to = scanner.nextInt();int value = scanner.nextInt();distance[from][to] = value;distance[to][from] = value;}floyd();}
}
5.SPFA
作用:
1.用于求带负权的边的单元最短路径,但是不能有负环
2.可以用来判断是否存在负环
判断负环:给每一个节点记录一个count,用来记录该结点入队的次数,如果count>n就说明存在负环
import java.util.*;
public class SPFA {static class Edge {int next;int value;public Edge(int next, int value) {this.next = next;this.value = value;}}static int[] distance;static int[] count;static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();static int N;static int M;public static void addNode(int node) {if (!graph.containsKey(node)) {graph.put(node, new LinkedList<>());}}public static void addEdge(int from, int to, int value) {if (!graph.containsKey(from)) {addNode(from);}if (!graph.containsKey(to)) {addNode(to);}graph.get(from).add(new Edge(to, value));graph.get(to).add(new Edge(from, value));}public static void spfa(int start) {Arrays.fill(distance, Integer.MAX_VALUE);distance[start] = 0;LinkedList<Integer> queue = new LinkedList<>();queue.add(start);count[start] = 1;while (!queue.isEmpty()) {//1.出队int node = queue.removeFirst();LinkedList<Edge> edges = graph.get(node);//2.遍历所有出边for (Edge edge : edges) {int next = edge.next;int value = edge.value;//3.如果可以更新则更新if (distance[next] > distance[node] + value) {distance[next] = distance[node] + value;//4.发现没在队列里面就添加进去if (!queue.contains(next)) {queue.add(next);//5.判断是否有负环count[next]++;if (count[next] > N) {System.out.println("存在负环");return;}}}}}System.out.println(Arrays.toString(distance));}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();for (int i = 0; i < M; i++) {int from = scanner.nextInt();int to = scanner.nextInt();int value = scanner.nextInt();addEdge(from, to, value);}distance = new int[N + 1];count = new int[N + 1];spfa(1);}
}
6.拓扑排序
拓扑排序只能用于有向无环图。如果程序结束,inDegree数字中存在非零元素,说明存在环路。
import java.util.*;
public class Main {public static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();static int N;static int M;static int[] inDegree;static boolean[] visited;public static void addEdge(int from, int to) {if (!graph.containsKey(from)) {graph.put(from, new LinkedList<>());}if (!graph.containsKey(to)) {graph.put(to, new LinkedList<>());}graph.get(from).add(to);}public static void topo() {//如果要求字典序最小,那么可以使用优先队列实现LinkedList<Integer> queue = new LinkedList<>();for (int i = 1; i <= N; i++) {if (inDegree[i] == 0) {queue.add(i);}}while (!queue.isEmpty()) {int node = queue.removeFirst();System.out.print(node+" ");LinkedList<Integer> edges = graph.get(node);for (Integer edge : edges) {inDegree[edge]--;if (inDegree[edge]==0){queue.add(edge);}}}System.out.println(Arrays.toString(inDegree));}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();inDegree = new int[N + 1];visited = new boolean[N + 1];for (int i = 0; i < M; i++) {int from = scanner.nextInt();int to = scanner.nextInt();inDegree[to]++;addEdge(from, to);}topo();}
}
7.欧拉回路与欧拉回路(有向图)
欧拉回路:是指通过图的每一条边恰好一次,且能回到起点的路径。要求起始点和终点一致。
对于一个有向图来说:
①如果每个节点都有入度-出度=0,则该图存在欧拉回路
欧拉路径:是指通过图的每一条边恰好一次,但不要求能回到起点的路径。不要求起始点和终点一致。
对于一个有向图来说:
①如果每个节点都有入度-出度=0,那么该图一定存在欧拉路径;
②如果只有一个节点入度-出度=1,只有一个入度-出度=-1,其余结点入度-出度=0,那么该图一定存在欧拉路径。
注意:如果有欧拉回路,那么求出的欧拉路径就是欧拉回路
public class Main {static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();static int N;static int M;//对于有向图而言static int[] inDegree;static int[] outDegree;//记录欧拉路径的起点,如果有欧拉回路的话,起点无所谓,就当做1static int oula_start = 1;static int oula_end = 1;//记录欧拉路径//如果有欧拉回路,那么求得的欧拉路径就是欧拉回路static LinkedList<Integer> path = new LinkedList<>();static void addEdge(int from, int to) {if (!graph.containsKey(from)) {graph.put(from, new LinkedList<>());}if (!graph.containsKey(to)) {graph.put(to, new LinkedList<>());}graph.get(from).add(to);}static boolean have() {int count_fu1 = 0;int count_1 = 0;boolean result = false;for (int i = 1; i <= N; i++) {if (inDegree[i] - outDegree[i] == 1) {count_1++;oula_end = i;} else if (inDegree[i] - outDegree[i] == -1) {oula_start = i;count_fu1++;}}if ((count_fu1 == 0 && count_1 == 0)) {System.out.println("有欧拉回路");result = true;} else if (count_fu1 == 1 && count_1 == 1) {System.out.println("没有欧拉回路,有欧拉路径");System.out.println("起点为" + oula_start);System.out.println("终点为" + oula_end);result = true;} else {System.out.println("没有欧拉路径");}return result;}public static void findPath(int node) {LinkedList<Integer> edges = graph.get(node);while (!edges.isEmpty()) {int next = edges.get(0);edges.remove(Integer.valueOf(next));findPath(next);}path.add(node);}//有向图public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();inDegree = new int[N + 1];outDegree = new int[N + 1];for (int i = 0; i < M; i++) {int from = scanner.nextInt();int to = scanner.nextInt();addEdge(from, to);outDegree[from]++;inDegree[to]++;}//1.判断是否有欧拉路径have();//2.求解欧拉路径//无向图求出的路径可以直接打印,有向图求出的路径需要逆序输出findPath(oula_start);//3.判断最后的边数if (path.size() == M + 1) {//逆序输出for (int i = path.size() - 1; i >= 0; i--) {System.out.print(path.get(i) + " ");}} else {System.out.println("没有欧拉路径");}}
}
8.欧拉回路和欧拉路径(无向图)
欧拉回路:是指通过图的每一条边恰好一次,且能回到起点的路径。要求起始点和终点一致
对于一个无向图来说:
①如果每个节点的度数都是偶数,则该图存在欧拉回路;
欧拉路径:是指通过图的每一条边恰好一次,但不要求能回到起点的路径。不要求起始点和终点一致.
对于一个无向图来说:
①如果每个节点的度数都是偶数,那么该图一定存在欧拉路径;
②如果只有两个度数为奇数的节点,那么该图一定存在欧拉路径;
public class Main {static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();static int N;static int M;static int oula_strat = 1;static int oula_end = 1;static int[] Degree;static LinkedList<Integer> path = new LinkedList<>();static void addEdge(int from, int to) {if (!graph.containsKey(from)) {graph.put(from, new LinkedList<>());}if (!graph.containsKey(to)) {graph.put(to, new LinkedList<>());}graph.get(from).add(to);graph.get(to).add(from);}public static void have() {boolean first = true;int odd_count = 0;for (int i = 1; i <= N; i++) {if (Degree[i] % 2 != 0) {odd_count++;if (first) {oula_strat = i;first = false;} else {oula_end = i;}}}if (odd_count == 0) {System.out.println("存在欧拉回路");} else if (odd_count == 2) {System.out.println("不存在欧拉回路,但存在欧拉路径,起点为:" + oula_strat + ",终点为:" + oula_end);} else {System.out.println("不存在欧拉路径");}}static void findPath(int node) {LinkedList<Integer> edges = graph.get(node);while (!edges.isEmpty()) {int next = edges.get(0);//注意无向图这里,需要删除两次边edges.remove(Integer.valueOf(next));graph.get(next).remove(Integer.valueOf(node));findPath(next);}//注意:是在回溯之前加入path.add(node);}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);N = scanner.nextInt();M = scanner.nextInt();Degree = new int[N + 1];for (int i = 0; i < M; i++) {int from = scanner.nextInt();int to = scanner.nextInt();addEdge(from, to);Degree[from]++;Degree[to]++;}//1.判断是否存在欧拉回路或者欧拉路径have();//2.找欧拉回路或者欧拉路径findPath(oula_strat);//3.判断最后的边数if (path.size() == M + 1) {System.out.println(path.toString());} else {System.out.println("没有欧拉路径");}}
}