代码随想录算法训练营第 49 天 | 98. 可达路径 / 797. 所有可能的路径

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。
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值