【图论】岛屿数量/腐烂的橘子/课程表/实现前缀树

目录标题

1.岛屿数量⭐

原题链接

(1)DFS

把每一个 '1' 看成图中的一个节点。如果两个 '1' 上下左右相邻,那么它们之间就有一条边。

遍历整个二维数组

  • 如果当前位置是 '0', 跳过
  • 如果当前位置是 '1', 说明发现了一个新的岛屿 count++
    • 然后使用 DFS,把和当前岛屿连在一起的所有 '1', 全部标记掉
java 复制代码
int res = 0;
public int numIslands(char[][] grid) {
    for (int i = 0; i < grid.length; i++) {
        for (int j = 0; j < grid[0].length; j++) {
            if (grid[i][j] == '1') {
                res++;
                dfs(grid, i, j);
            }
        }
    }
    return res;
}
public void dfs(char[][] grid, int i, int j) {
	//越界或者已经是水了
    if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == '0') {
        return;
    }
    grid[i][j] = '0';
    dfs(grid, i + 1, j);
    dfs(grid, i - 1, j);
    dfs(grid, i, j + 1);
    dfs(grid, i, j - 1);
}
  • 每个位置都需要处理一次,时间复杂度:O(m × n)
  • 最坏情况下整个地图都是陆地,递归栈:空间复杂度:O(m × n)

(2)BFS

java 复制代码
public int numIslands(char[][] grid) {
    int m = grid.length;
    int n = grid[0].length;
    int count = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            // 发现一个新的岛屿
            if (grid[i][j] == '1') {
                count++;
                bfs(grid, i, j);
            }
        }
    }
    return count;
}
private void bfs(char[][] grid, int i, int j) {
    int m = grid.length;
    int n = grid[0].length;
    Queue<int[]> queue = new ArrayDeque<>();
    // 起点入队
    queue.offer(new int[]{i, j});
    // 标记为已经访问
    grid[i][j] = '0';
    // 四个方向:上、下、左、右
    int[][] directions = {
        {-1, 0},
        {1, 0},
        {0, -1},
        {0, 1}
    };
    while (!queue.isEmpty()) {
        int[] cur = queue.poll();
        int x = cur[0];
        int y = cur[1];
        // 遍历四个方向
        for (int[] direction : directions) {
            int newX = x + direction[0];
            int newY = y + direction[1];
            // 不越界,并且是陆地
            if (newX >= 0 && newX < m
                    && newY >= 0 && newY < n
                    && grid[newX][newY] == '1') {

                // 标记已经访问
                grid[newX][newY] = '0';

                // 加入队列
                queue.offer(new int[]{newX, newY});
            }
        }
    }
}

2.腐烂的橘子

原题链接

(1)多源 BFS

所有初始腐烂橘子同时加入队列

首先遍历整个 grid

  • 遇到 1 fresh++ ;遇到 2: 加入 queue 然后 BFS。
  • 每一轮处理当前队列中的所有橘子:
  • while 队列不为空,表示当前这一层有 size 个腐烂橘子
    • 遍历这 size 个橘子,检查上下左右, 如果邻居是新鲜橘子 1 就变成腐烂 2,fresh--,加入队列
    • 如果这一轮真的腐烂了新橘子 minutes++
  • 最后如果 fresh == 0,返回 minutes。否则返回 -1
java 复制代码
public int orangesRotting(int[][] grid) {
    Queue<int[]> queue = new LinkedList<>();
    int n = grid.length;
    int m = grid[0].length;
    int fresh = 0;
    int minutes = 0;
    //先将所有腐烂的橘子加入队列中
    //并统计新鲜的橘子数量
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 2) {
                queue.offer(new int[]{i, j});
            }
            else  if (grid[i][j] == 1) {
                fresh++;
            }
        }
    }
    // 四个方向
    int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    //开始bfs
    //每次处理一轮队列中的烂橘子
    //同时减少新鲜的橘子数量
    while (!queue.isEmpty() && fresh>0) {
        int size = queue.size();
        //当前一层的处理
        for (int i = 0; i < size; i++) {
            int[] cur = queue.poll();
            //将当前烂橘子的上下左右四个方向的橘子加入队列中
            //并减少新鲜的橘子数量
            for (int[] dir : directions) {
                int nx = cur[0] + dir[0];
                int ny = cur[1] + dir[1];
                if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
                    if (grid[nx][ny] != 1) {
                        continue;
                    }
                    fresh--;
                    grid[nx][ny] = 2;
                    queue.offer(new int[]{nx, ny});
                }
            }
        }
        minutes++;
    }
    return fresh==0?minutes:-1;
}

3.课程表

原题链接

(1)有向图 + 拓扑排序 + 判断是否有环

  • 入度 indegree 表示还有多少门前置课程没有学完。
    • 如下例所示,课程 1 有两个前置课程。所以indegree1 = 2
    • 首先找所有入度 = 0的课程。这些课程没有任何前置课程,可以直接学。
    • 入度 = 0的课程全部加入队列,然后开始BFS
java 复制代码
0 -> 1
2 -> 1
  • BFS过程
    • 首先取出一门当前可以学习的课程,把它学掉
    • 它的所有后续课程入度 -1
    • 如果某门课入度变成0说明它现在也可以学习了加入队列
  • 最后如果学完数量 == numCourses,表示不存在环可以学完
java 复制代码
public static boolean canFinish(int numCourses, int[][] prerequisites) {
    List<List<Integer>> graph = new ArrayList<>();
    //简历邻接图
    for(int i=0;i<numCourses;i++) {
        graph.add(new ArrayList<>());
    }

    // 计算入度
    int[] indegree = new int[numCourses];
    for(int[] edge : prerequisites) {
        //edge[x][1]->edge[x][0]
        graph.get(edge[1]).add(edge[0]);
        indegree[edge[0]]++;
    }

    // 入度为0的点入队
    Queue<Integer> queue = new ArrayDeque<>();
    for(int i = 0; i < numCourses; i++) {
        if(indegree[i] == 0) {
            queue.offer(i);
        }
    }

    //  BFS拓扑排序
    // 如果有一个环,那么入度就会减到负数,这时队列为空
    while(!queue.isEmpty()) {
        int course = queue.poll();
        numCourses--;
        //所有连接的节点入度减一
        for(int i : graph.get(course)) {
            indegree[i]--;
            if(indegree[i] == 0) {
                queue.offer(i);
            }
        }
    }
    return numCourses == 0;
}

4.实现 Trie (前缀树)

原题链接

(1)字符树

用一棵"字符树"来存字符串,公共前缀只存一份

java 复制代码
public class Trie {
    // 字典树节点
    private class TrieNode {
        private boolean isEnd;
        private TrieNode[] children = new TrieNode[26];
    }

    private TrieNode root;

    // 构造函数典树
    public Trie() {
        root = new TrieNode();
    }

    // 插入单词
    public void insert(String word) {
        TrieNode cur = root;
        for(char c : word.toCharArray()) {
            int i = c - 'a';
            if(cur.children[i] == null) {
                cur.children[i] = new TrieNode();
            }
            cur = cur.children[i];
        }
        cur.isEnd = true;
    }

    // 搜索单词
    public boolean search(String word) {
        TrieNode cur = root;
        for(char c : word.toCharArray()) {
            int i = c - 'a';
            if(cur.children[i] == null) {
                return false;
            }
            cur = cur.children[i];
        }
        return cur.isEnd;
    }

    // 搜索前缀
    public boolean startsWith(String prefix) {
        TrieNode cur = root;
        for(char c : prefix.toCharArray()) {
            int i = c - 'a';
            if(cur.children[i] == null) {
                return false;
            }
            cur = cur.children[i];
        }
        return true;
    }
}
相关推荐
lvwangshu2 天前
图论:LCA、树的直径、树的重心、二分图与 Tarjan 缩点
算法·图论
hansang_IR4 天前
【题解】[APIO2023] 赛博乐园 / cyberland
c++·算法·图论
aqiu1111114 天前
【并查集 / 图论】蓝桥云课 - 魔法大陆的城市群(求无向图连通块数量)题解
蓝桥杯·图论
lvwangshu9 天前
P6534 [COCI 2015/2016 #1] UZASTOPNI 等差树列 题解
动态规划·图论·题解·性质题
positive_zpc12 天前
进阶数据结构图——关键路径(四)
数据结构·图论·关键路径
云淡风轻~窗明几净13 天前
宇宙管理学猜想
算法·图论
小星星闪亮登场13 天前
图论--最小生成树(内含二分图)
数据结构·算法·图论·迭代加深·图搜索算法
positive_zpc13 天前
进阶数据结构图——最短路径(二)
数据结构·算法·图论·最短路径
positive_zpc13 天前
进阶数据结构图——最小生成树(一)
数据结构·算法·图论