1.邻接表
java
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
求单源最短路,不能求带有负权边
java
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=true
visited[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优化版本
java
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;
}
@Override
public 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])
java
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就说明存在负环
java
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数字中存在非零元素,说明存在环路。
java
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,那么该图一定存在欧拉路径。
注意:如果有欧拉回路,那么求出的欧拉路径就是欧拉回路
java
public class Main {
static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();
static int N;
static int M;
//对于有向图而言
static int[] inDegree;
static int[] outDegree;
//记录欧拉路径的起点,如果有欧拉回路的话,起点无所谓,就当做1
static 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.欧拉回路和欧拉路径(无向图)
欧拉回路 :是指通过图的每一条边恰好一次,且能回到起点的路径。要求起始点和终点一致
对于一个无向图来说:
①如果每个节点的度数都是偶数,则该图存在欧拉回路;
欧拉路径 :是指通过图的每一条边恰好一次,但不要求能回到起点的路径。不要求起始点和终点一致.对于一个无向图来说:
①如果每个节点的度数都是偶数,那么该图一定存在欧拉路径;
②如果只有两个度数为奇数的节点,那么该图一定存在欧拉路径;
java
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("没有欧拉路径");
}
}
}