98. 可达路径 / 797. 所有可能的路径
ACM 模式 题目链接
或
LeetCode 模式 题目链接
注意:有向无环图不需要 visited 数组。
需要提前在 path 中把节点 1 加上。
(因为节点从 1 到 n)
ACM 模式,节点从 1 到 n,邻接矩阵表示图:
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
public class Main {
public static List<List<Integer>> result = new ArrayList<>();
public static List<Integer> path = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] graph = new int[n + 1][n + 1];
while (m-- > 0) {
int s = sc.nextInt();
int t = sc.nextInt();
graph[s][t] = 1;
}
path.add(1); // 必须先把节点 1 加上
dfs(graph, 1, n);
for (List pa : result) {
for (int i = 0; i < pa.size() - 1; i++) {
System.out.print(pa.get(i) + " ");
}
System.out.println(pa.get(pa.size() - 1));
}
if (result.isEmpty()) {
System.out.println(-1);
}
}
// x 为当前遍历的节点,n 为终点
public static void dfs(int[][] graph, int x, int n) {
if (x == n) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 1; i <= n; i++) {
if (graph[x][i] == 1) {
path.add(i);
dfs(graph, i, n);
path.remove(path.size() - 1); // 记得回溯
}
}
}
}
ACM 模式,节点从 1 到 n,邻接表表示图:
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
public class Main {
public static List<List<Integer>> result = new ArrayList<>();
public static List<Integer> path = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
List<List<Integer>> graph = new ArrayList<>(n + 1);
for (int i = 0; i <= n; i++) { // 必须先放入 n + 1 个空列表
graph.add(new ArrayList<>());
}
while (m-- > 0) {
int s = sc.nextInt();
int t = sc.nextInt();
graph.get(s).add(t);
}
path.add(1); // 必须先把节点 1 加上
dfs(graph, 1, n);
for (List pa : result) {
for (int i = 0; i < pa.size() - 1; i++) {
System.out.print(pa.get(i) + " ");
}
System.out.println(pa.get(pa.size() - 1));
}
if (result.isEmpty()) {
System.out.println(-1);
}
}
// x 为当前遍历的节点,n 为终点
public static void dfs(List<List<Integer>> graph, int x, int n) {
if (x == n) {
result.add(new ArrayList<>(path));
return;
}
for (int i : graph.get(x)) {
path.add(i);
dfs(graph, i, n);
path.remove(path.size() - 1); // 记得回溯
}
}
}
LeetCode 模式,节点从 0 到 n - 1,领接表表示矩阵:
如 graph = [[1,2],[3],[3],[]],在 Java 中为锯齿数组。
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
path.add(0);
dfs(graph, 0, graph.length - 1);
return result;
}
public void dfs(int[][] graph, int cur, int end) {
if (cur == end) {
result.add(new ArrayList<>(path));
return;
}
for (int target : graph[cur]) {
if (target >= 0) { // Java 支持锯齿数组,所以不需要这一行,不会用 -1 填充
path.add(target);
dfs(graph, target, end);
path.remove(path.size() - 1);
}
}
}
}
深度优先搜索和广度优先搜索总结
深度优先搜索:
代码类似于回溯法。
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
public class Main {
public static List<List<Integer>> result = new ArrayList<>();
public static List<Integer> path = new ArrayList<>();
// x 为当前遍历的节点,n 为终点
public static void dfs(图, 目前搜索的节点) {
if (终止条件) {
存放结果
return;
}
for (选择本节点连接的节点) {
处理节点
dfs(图,选择的节点)
回溯,撤销处理结果
}
}
}
如:
一般图 / 抽象图(General Graph):
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
public class Main {
public static List<List<Integer>> result = new ArrayList<>();
public static List<Integer> path = new ArrayList<>();
// x 为当前遍历的节点,n 为终点。节点 1~n,有向无环图
public static void dfs(int[][] graph, int x, int n) {
if (x == n) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 1; i <= n; i++) {
if (graph[x][i] == 1) {
path.add(i);
dfs(graph, i, n);
path.remove(path.size() - 1); // 记得回溯
}
}
}
}
网格图 / 栅格图(Grid Graph)——二维网格的特殊图:
import java.util.*;
public class Main {
public static int[][] dir = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
public static void dfs(int[][] grid, boolean[][] visited, int x, int y) {
if (grid[x][y] == 0 || visited[x][y]) { // 当不是陆地或者已经访问过就退出
return;
}
visited[x][y] = true; // 判断完终止条件后,再赋值为 true
for (int i = 0; i < 4; i++) {
int nextX = dir[i][0] + x;
int nextY = dir[i][1] + y;
if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) {
continue;
}
dfs(grid, visited, nextX, nextY); // 不用判断陆地和访问,直接 dfs
}
}
}
广度优先搜索:
网格图 / 栅格图(Grid Graph)——二维网格的特殊图
陷阱:
队列刚一放进去元素,visited 数组就要立马标记为 true。
代码类似于二叉树层序遍历。
import java.util.*;
public class Main {
int[][] dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向
public void bfs(int[][] grid, boolean[][] visited, int x, int y) { // (x, y) 为起点坐标
Queue<int[]> queue = new ArrayDeque<>();
queue.offer(new int[] {x, y}); // 插入起点
visited[x][y] = true; // 队列刚一放进去元素,visited 数组就要立马标记为 true。
while (!queue.isEmpty()) {
int[] cur = queue.poll(); // 拿出第一个元素
for (int i = 0; i < 4; i++) {
int nextX = cur[0] + dir[i][0];
int nextY = cur[1] + dir[i][1];
// 判断未越界
if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) {
continue;
}
// 判断未访问过
if (!visited[nextX][nextY]) {
queue.offer(new int[] {nextX, nextY});
visited[nextX][nextY] = true; // 队列刚一放进去元素,visited 数组就要立马标记为 true。
}
}
}
}
}
876

被折叠的 条评论
为什么被折叠?



