代码随想录算法训练营第四十八天 | 图 | 深度搜索 | 广度搜索

Day 48 总结

  • 自己实现中遇到哪些困难
  • 今日收获,记录一下自己的学习时间
    • 11:40 - 14:30

图论

深度收缩 & 广度搜索
并查集
最小生成树
拓扑排序
最短路径算法

图论基础

    • 二维空间里,多个点之间相互连接
  • 图的种类
    • 有向图
    • 无向图
    • 加权
  • 度 Degree
    • 连接某个节点的边的数量
    • 出度,入度
  • 连通性
    • 节点联通情况
    • 连通图
      • 任意两个节点存在一条路径
    • 强连通图
      • 任务两个节点可以相互到达
    • 连通分量
      • 独立的连通子图(无向图中的极大连通子图)
    • 强连通分量
      • 有向图中极大强连通子图称之为该图的强连通分量
  • 图的构造
    • 邻接表
      • 数组 + 链表
      • 适用于稀疏图, 空间利用率高
    • 邻接矩阵
      • 二维数组
      • 表达简单,检查连接的速度块,适合稠密图
  • 图的遍历方式
    • 深度优先 dfs
    • 广度优先 bfs

深度优先搜索

  • 基本理解
    • 搜索方向,路径撤销(回溯)
  • 代码框架
复制代码
  void dfs(参数) {
      if (终止条件) {
          存放结果;
          return;
      }

      for (选择:本节点所连接的其他节点) {
          处理节点;
          dfs(图,选择的节点); // 递归
          回溯,撤销处理结果
      }
  }

98. 所有可达路径

题目连接:98. 所有可达路径

题目描述:

给定一个有 n 个节点的有向无环图,节点编号从 1 到 n。请编写一个函数,找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。

输入:

N个节点,M个边

s节点 与 t节点 是相连的

输出:

所有从1出发的路径 找出并返回所有从节点 1 到节点 n 的路径

实现思路:

用 N*N 的邻接表保存节点的连接信息,通过dfs收集所有路径。

使用visted变量记录当前访问过哪些节点,用于撤销操作 有向无环,不需要visted记录

使用for循环遍历邻接表中当前节点的行,搜索所有相邻节点

path变量保存临时搜索路径,result变量收集所有路径

代码实现:
java 复制代码
public class Main {
    public static List<Integer> path = new ArrayList<>();
    public static List<List<Integer>> result = new ArrayList<>();
    public static int[][] graph;

    public static void main (String[] args) {
        /* code */
        Scanner in = new Scanner(System.in);
        String[] firstLine = in.nextLine().split(" ");
        int N = Integer.valueOf(firstLine[0]);
        int M = Integer.valueOf(firstLine[1]);

        graph = new int[N+1][N+1];
        visited = new boolean[N+1];
        for (int i=0; i<M; i++) {
            String[] line = in.nextLine().split(" ");
            int row = Integer.valueOf(line[0]);
            int col = Integer.valueOf(line[1]);
            graph[row][col] = 1;
        }        
        
        path.add(1);
        dfs1(1, N);
       
        if (result.size() == 0) 
            System.out.println(-1);
        else {
            for (List<Integer> p : result) {
                for (int i=0; i<p.size()-1; i++)
                    System.out.print(p.get(i) + " ");
                System.out.println(p.get(p.size()-1));
            }            
        }

    }
    
    public static void dfs1 (int node, int N) {
        // 到达路径末尾,收集路径
        if (node == N) { // 找到从1到N的路径
            result.add(new ArrayList<>(path));
            return;
        }

        for (int i=1; i<=N; i++) {
            if (graph[node][i] == 0) continue;
            path.add(i);
            dfs1(i, N);
            path.remove(path.size()-1);
        }            
    }

}

99. 岛屿数量

题目连接:99. 岛屿数量

题目描述:

给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

输入:

N行,M列的矩阵

矩阵的具体数字,1代表一个岛,0代表水。

输出:

输出一个整数,表示岛屿的数量。如果不存在岛屿,则输出 0。

实现思路:

使用一个visted矩阵,标记都有哪些岛屿是访问过的。

按序访问每一个未访问过的岛屿,以该岛屿为起始点,进行深度搜索, 本次dfs结束之后,孤岛数量+1

深度搜索:

确定四个方向,上,右,下,左

分别dfs这四个分支

代码实现:
java 复制代码
public class Main {

    public static void main (String[] args) {
        /* code */
        Scanner in = new Scanner(System.in);
        String[] firstLine = in.nextLine().split(" ");
        int N = Integer.valueOf(firstLine[0]);
        int M = Integer.valueOf(firstLine[1]);
        int[][] graph = new int[N][M];
        boolean[][] visited = new boolean[N][M];
        for (int i=0; i<N; i++) {
            for (int j=0; j<M; j++) {
                graph[i][j] = in.nextInt();   
            }
        }        
        
        // for (int i=0; i<N; i++) {
        //     System.out.println(Arrays.toString(graph[i]));
        // }
        int ct = 0;
        for (int i=0; i<N; i++) {
            for (int j=0; j<M; j++) {
                if (graph[i][j] == 1 && !visited[i][j]) {
                    // System.out.println("start:" + i + " " + j);
                    dfs(graph, visited,i,j, N, M);
                    ct++;
                }
            }
        }

        System.out.println(ct);
    }

    public static void dfs(int[][] graph, boolean[][] visited, int x, int y, int N, int M) {
        int[] directions = {-1,0, 0,1, 1,0, 0,-1}; // 上右下左
        visited[x][y] = true;

        for (int i=0; i<4; i++) {
            int directX = directions[i*2];
            int directY = directions[i*2+1];

            if (directX+x >= 0 && directX+x < N && directY+y >= 0 && directY+y < M &&
                graph[directX+x][directY+y]==1 && !visited[directX+x][directY+y]) {
                // System.out.println("dfs:" + (directX+x) + " " + (directY+y));
                dfs(graph, visited, directX+x, directY+y, N, M);
            }
        }
    }
    

}

广度优先搜索

  • BFS, 求点与点之间最短路径,以起始点为圆心,按圈搜索

  • 搜索过程

    • 起始点,起始点的四个方向
    • 队列保存顺(逆)时针的邻居
    java 复制代码
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
    // grid 是地图,也就是一个二维数组
    // visited标记访问过的节点,不要重复访问
    // x,y 表示开始搜索节点的下标
    void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
        queue<pair<int, int>> que; // 定义队列
        que.push({x, y}); // 起始节点加入队列
        visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
        while(!que.empty()) { // 开始遍历队列里的元素
            pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素
            int curx = cur.first;
            int cury = cur.second; // 当前节点坐标
            for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
                int nextx = curx + dir[i][0];
                int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
                if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 坐标越界了,直接跳过
                if (!visited[nextx][nexty]) { // 如果节点没被访问过
                    que.push({nextx, nexty});  // 队列添加该节点为下一轮要遍历的节点
                    visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
                }
            }
        }
    
    }
99. 岛屿数量

题目连接:99. 岛屿数量

题目描述:

给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

输入:

N行,M列的矩阵

矩阵的具体数字,1代表一个岛,0代表水。

输出:

输出一个整数,表示岛屿的数量。如果不存在岛屿,则输出 0。

实现思路:

使用一个visted矩阵,标记都有哪些岛屿是访问过的。

按序访问每一个未访问过的岛屿,以该岛屿为起始点,进行广度搜索, 本次bfs结束之后,孤岛数量+1

广度搜索:

确定四个方向,上,右,下,左

使用一个队列将这个四个岛屿存储起来,然后bfs

代码实现:
java 复制代码
public class Main {
    public static void main (String[] args) {
        /* code */
        Scanner in = new Scanner(System.in);
        String[] firstLine = in.nextLine().split(" ");
        int N = Integer.valueOf(firstLine[0]);
        int M = Integer.valueOf(firstLine[1]);
        int[][] graph = new int[N][M];
        boolean[][] visited = new boolean[N][M];
        for (int i=0; i<N; i++) {
            for (int j=0; j<M; j++) {
                graph[i][j] = in.nextInt();   
            }
        }        
        

        int ct = 0;
        for (int i=0; i<N; i++) {
            for (int j=0; j<M; j++) {
                if (graph[i][j] == 1 && !visited[i][j]) {
                    // System.out.println("start:" + i + " " + j);
                    bfs(graph, visited,i,j, N, M);
                    ct++;
                }
            }
        }
        System.out.println(ct);
    }

    public static void bfs(int[][] graph, boolean[][] visited, int X, int Y, int N, int M) {
        int[] directions = {-1,0, 0,1, 1,0, 0,-1}; // 上右下左
        Deque<int[]> queue = new LinkedList<>();

        queue.add(new int[]{X, Y});
        visited[X][Y] = true;

        while (!queue.isEmpty()) {
            int[] land = queue.poll();
            int x = land[0];
            int y = land[1];
            
            for (int i=0; i<4; i++) {
                int directX = directions[i*2];
                int directY = directions[i*2+1];

                if (directX+x >= 0 && directX+x < N && directY+y >= 0 && directY+y < M &&
                    graph[directX+x][directY+y]==1 && !visited[directX+x][directY+y]) {

                    queue.add(new int[]{directX+x,directY+y});
                    visited[directX+x][directY+y] = true;
                }
            }

        }

    }
    

}
相关推荐
山北雨夜漫步1 小时前
机器学习 Day14 XGboost(极端梯度提升树)算法
人工智能·算法·机器学习
到底怎么取名字不会重复1 小时前
Day10——LeetCode15&560
c++·算法·leetcode·哈希算法·散列表
chuxinweihui2 小时前
数据结构——二叉树,堆
c语言·开发语言·数据结构·学习·算法·链表
freexyn2 小时前
Matlab自学笔记五十一:(推荐)输入参数的数量和可变数量的输入
笔记·算法·matlab
陈大大陈2 小时前
基于 C++ 的用户认证系统开发:从注册登录到Redis 缓存优化
java·linux·开发语言·数据结构·c++·算法·缓存
数据分析螺丝钉2 小时前
LeetCode 252 会议室 III(Meeting Rooms III)题解与模拟面试
算法·leetcode·职场和发展
小李独爱秋2 小时前
动态哈希映射深度指南:从基础到高阶实现与优化
数据结构·算法·哈希算法
猫猫头有亿点炸2 小时前
C语言斐波拉契数列2.0
c语言·开发语言·算法
写个博客3 小时前
代码随想录算法训练营第二十六天
算法
海底火旺3 小时前
寻找缺失的最小正整数:从暴力到最优的算法演进
javascript·算法·面试