Leecode Hot75

图论

Solution200. 岛屿数量

这里是引用

如图,有三处岛屿

这里是引用

/**
 * 一开始,从[0][0]处开始遍历,一开始遍历到了左上角的那座岛屿
 *  从[0][0]开始,把[0][0]由1处理为0,然后遍历[0][0]的上下左右 
 *                遍历[0][0]的上:[-1][0],越界了,退出
 *                遍历[0][0]的下:[1][0]的位置是1,把[1][0]由1处理为0,然后遍历[1][0]的上下左右 
 *                                                                 遍历[1][0]的上:[0][0]位置已经是0了,退出
 *                                                                 遍历[1][0]的下:[2][0]位置已经是0了,退出
 *                                                                 遍历[1][0]的左:[1][-1]位置越界了,退出
 *                                                                 遍历[1][0]的右:[1][1]的位置是1,把[1][1]由1处理为0,然后遍历[1][1]的上下左右 
 *                                                                                                                   遍历[1][1]的上:[0][1]的位置是1,把[0][1]由1处理为0,然后遍历[0][1]的上下左右([0][1]的上下左右均是0,退出)
 *                                                                                                                   遍历[1][1]的下:[2][1]位置已经是0了,退出
 *                                                                                                                   遍历[1][1]的左:[1][0]位置已经是0了,退出
 *                                                                                                                   遍历[1][1]的右:[1][2]位置已经是0了,退出
 *                遍历[0][0]的左:[0][-1],越界了,退出
 *                遍历[0][0]的右:[0][1]位置已经是0了,退出
 *                第一座岛屿的已经全是0了,第一座岛屿遍历结束
 *                
 *  图解第一座岛屿的变化过程:
 *                          [0][0]由1处理为0                    把[1][0]由1处理为0                          [1][1]由1处理为0                       [0][1]由1处理为0                           第一座岛屿的已经全是0了,第一座岛屿遍历结束
 *  {'1', '1', '0', '0', '0'},           {'0', '1', '0', '0', '0'},             {'0', '1', '0', '0', '0'},              {'0', '1', '0', '0', '0'},              {'0', '0', '0', '0', '0'},
 *  {'1', '1', '0', '0', '0'},           {'1', '1', '0', '0', '0'},             {'0', '1', '0', '0', '0'},              {'0', '0', '0', '0', '0'},              {'0', '0', '0', '0', '0'},
 *  {'0', '0', '1', '0', '0'},  ———>     {'0', '0', '1', '0', '0'},   ———>      {'0', '0', '1', '0', '0'},   ———>       {'0', '0', '1', '0', '0'},      ———>    {'0', '0', '1', '0', '0'},
 *  {'0', '0', '0', '1', '1'}            {'0', '0', '0', '1', '1'}              {'0', '0', '0', '1', '1'}               {'0', '0', '0', '1', '1'}               {'0', '0', '0', '1', '1'} 
 */
public class Solution {
   
   
    public int numIslands(char[][] grid) {
   
   
        if (grid == null || grid.length == 0) return 0;

        int count = 0;
        int rows = grid.length;
        int cols = grid[0].length;

        for (int i = 0; i < rows; i++) {
   
   
            for (int j = 0; j < cols; j++) {
   
   
                if (grid[i][j] == '1') {
   
   
                    // 发现一个新的岛屿,开始DFS
                    dfs(grid, i, j);
                    count++; // 每完成一次DFS,岛屿数量增加1
                }
            }
        }

        return count;
    }

    // 深度优先搜索函数,将当前陆地及其相邻的陆地全部标记为已访问('0')
    private void dfs(char[][] grid, int row, int col) {
   
   
        int rows = grid.length;
        int cols = grid[0].length;

        // 边界条件判断,或者当前位置已经访问过('0')
        if (row < 0 || row >= rows || col < 0 || col >= cols || grid[row][col] == '0') {
   
   
            return;
        }

        // 将当前位置标记为已访问
        grid[row][col] = '0';

        // 对当前陆地的上、下、左、右四个方向进行DFS
        dfs(grid, row - 1, col); // 上
        dfs(grid, row + 1, col); // 下
        dfs(grid, row, col - 1); // 左
        dfs(grid, row, col + 1); // 右
    }

    public static void main(String[] args) {
   
   
        Solution solution = new Solution();

        // 测试用例
        char[][] grid = {
   
   
                {
   
   '1', '1', '0', '0', '0'},
                {
   
   '1', '1', '0', '0', '0'},
                {
   
   '0', '0', '1', '0', '0'},
                {
   
   '0', '0', '0', '1', '1'}
        };
        
        int result = solution.numIslands(grid);
        System.out.println("岛屿数量: " + result); // 输出: 岛屿数量: 1
    }
}

Solution994. 腐烂的橘子

这里是引用
在这里插入图片描述

/**
 *      {2, 1, 1},
 *      {1, 1, 0},
 *      {0, 1, 1}
 *
 * 一开始,统计新鲜的橘子的数量=6,把腐烂的橘子的坐标[0][0]放到queue里面,queue的大小为1
 *    遍历queue里面的这一条数据
 *         从queue里面取出腐烂的橘子的坐标[0][0](取出来后queue为空了),遍历[0][0]的上下左右
 *             遍历[0][0]的上:[-1][0],越界了,退出
 *             遍历[0][0]的下:[1][0]的位置是1,把[1][0]由1处理为2,新鲜的橘子的数量=5,把腐烂的橘子的坐标[1][0]放到queue里面,queue里面的内容是[[1][0]]
 *             遍历[0][0]的左:[0][-1],越界了,退出
 *             遍历[0][0]的右:[0][1]的位置是1,把[0][1]由1处理为2,新鲜的橘子的数量=4,把腐烂的橘子的坐标[0][1]放到queue里面,queue里面的内容是[[1][0],[0][1]]
 *             [0][0]的上下左右遍历完毕,此时新鲜的橘子的数量=4,queue里面的内容是[[1][0],[0][1]],用时minutes=1,queue的大小为2
 *    遍历queue里面的这两条数据:
 *         从queue里面取出腐烂的橘子的坐标[1][0](取出来后queue内容是[[0][1]]),遍历[1][0]的上下左右
 *             遍历[1][0]的上:[0][0]的位置是2,退出
 *             遍历[1][0]的下:[2][0]的位置是0,退出
 *             遍历[1][0]的左:[1][-1],越界了,退出
 *             遍历[1][0]的右:[1][1]的位置是1,把[1][1]由1处理为2,新鲜的橘子的数量=3,把腐烂的橘子的坐标[1][1]放到queue里面,queue里面的内容是[[0][1],[1][1]]
 *             [1][0]的上下左右遍历完毕
 *         从queue里面取出腐烂的橘子的坐标[0][1](取出来后queue内容是[[1][1]]),遍历[0][1]的上下左右
 *             遍历[0][1]的上:[-1][1]越界了,退出
 *             遍历[0][1]的下:[1][1]的位置已经是2了,退出
 *             遍历[0][1]的左:[0][0]的位置已经是2了,退出
 *             遍历[0][1]的右:[0][2]的位置是1,把[0][2]由1处理为2,新鲜的橘子的数量=2,把腐烂的橘子的坐标[0][2]放到queue里面,queue里面的内容是[[1][1],[0][2]]
 *             [0][1]的上下左右遍历完毕
 *         第二遍遍历完毕,此时新鲜的橘子的数量=2,queue里面的内容是[[1][1],[0][2]],已经用时minutes=2,queue的大小为2
 *
 *  图解变化过程:
 *            1分钟后             2分钟后               3分钟后             4分钟后
 *  {2, 1, 1},          {2, 2, 1},           {2, 2, 2},         {2, 2, 2},          {2, 2, 2},
 *  {1, 1, 0},   ———>   {2, 1, 0},   ———>    {2, 2, 0},   ———>  {2, 2, 0},   ———>   {2, 2, 0},
 *  {0, 1, 1}           {0, 1, 1}            {0, 1, 1}          {0, 2, 1}           {0, 2, 2}
 */
public class Solution {
   
   
    private static final int[][] DIRECTIONS = {
   
   {
   
   -1, 0}, {
   
   1, 0}, {
   
   0, -1}, {
   
   0, 1}};

    public int orangesRotting(int[][] grid) {
   
   
        int rows = grid.length;
        int cols = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();
        int freshOranges = 0;

        // 初始化队列,将所有腐烂的橘子加入队列,并计算新鲜橘子的数量
        for (int i = 0; i < rows; i++) {
   
   
            for (int j = 0; j < cols; j++) {
   
   
                if (grid[i][j] == 2) {
   
   
                    queue.offer(new int[]{
   
   i, j});
                } else if (grid[i][j] == 1) {
   
   
                    freshOranges++;
                }
            }
        }

        int minutes = 0;
        // BFS遍历,直到队列为空
        while (!queue.isEmpty()) {
   
   
            int size = queue.size();
            // 遍历当前层的所有腐烂橘子
            for (int i = 0; i < size; i++) {
   
   
                int[] orange = queue.poll();
                int row = orange[0];
                int col = orange[1];

                // 检查四个方向上的相邻单元格
                for (int[] direction : DIRECTIONS) {
   
   
                    int newRow = row + direction[0];
                    int newCol = col + direction[1];

                    // 如果相邻单元格是新鲜橘子,则将其腐烂并加入队列,同时减少新鲜橘子的数量
                    if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && grid[newRow][newCol] == 1) {
   
   
                        grid[newRow][newCol] = 2;
                        queue.offer(new int[]{
   
   newRow, newCol});
                        freshOranges--;
                    }
                }
            }
            // 完成了一层遍历,分钟数加一
            if (!queue.isEmpty()) {
   
   
                minutes++;
            }
        }

        // 如果还有新鲜橘子剩余,则返回-1;否则返回分钟数
        return freshOranges == 0 ? minutes : -1;
    }

    public static void main(String[] args) {
   
   
        Solution solution = new Solution();
        int[][] grid = {
   
   
                {
   
   2, 1, 1},
                {
   
   1, 1, 0},
                {
   
   0, 1, 1}
        };
        int result = solution.orangesRotting(grid);
        System.out.println("经过的最小分钟数: " + result); // 输出应为4

        // 树的执行流程解释:
        // 1. 初始化队列和新鲜橘子计数,找到所有腐烂橘子入队。
        // 2. 开始BFS遍历,每次从队列中取出一个腐烂橘子位置。
        // 3. 检查该橘子周围四个方向上的新鲜橘子,如果找到,则腐烂该橘子,并将其位置入队。
        // 4. 每一轮BFS遍历代表经过了一分钟,分钟数加一。
        // 5. 当队列为空或没有新鲜橘子时,遍历结束。如果还有新鲜橘子则返回-1,否则返回分钟数减一。
    }
}

Solution207. 课程表

这里是引用

/**
 * 输入的课程如下:
 *   {1, 0},
 *   {2, 0},
 *   {3, 1},
 *   {3, 2}
 *
 *  构建课程依赖关系图
 *      0 -> 1 -> 3
 *      0 -> 2 -> 3
 *  表示学习课程 1 前要先学课程 0,学习课程 2 前要先学课程 0,学习课程 3 前要先学课程 1 和 2
 *
 *  思路:
 *    初始化,adjList=[[1,2],[3],[3],[]],numCourses=4表示有四门课,visted=[0,0,0,0]表示四个元素均未访问(visted可能是0或1或2,而0表示未曾访问,1表示正在访问,2表示访问完毕)
 *    一开始,对第0门课程进行学习,将visted[0]置为1此时visted=[1,0,0,0],拿到adjList的第0个元素[1,2](表示学习0之前需要学习1和2),所以遍历这俩元素:
 *                访问元素1,将visted[1]置为1此时visted=[1,1,0,0],访问adjList的第1个元素[3](表示学习1之前需要学习3),然后遍历这个元素:
 *                         访问元素3,将visted[3]置为1此时visted=[1,1,0,1],访问adjList的第3个元素[],由于adjList的第3个元素为空,所以将visted[3]置为2此时visted=[1,1,0,2]
 *                              adjList的第1个元素[3]访问完毕,之前说的学习1之前需要学习3,现在3学完了那么1可以学习了,学完后将visted[1]置为2此时visted=[1,2,0,2]
 *                访问元素2,将visted[2]置为1此时visted=[1,2,1,2],访问adjList的第2个元素[3](表示学习2之前需要学习3),然后遍历这个元素:
 *                         访问元素3,由于visted[3]已经是2了,说明这个元素已经访问过了,那就退出访问
 *                              adjList的第2个元素[3]访问完毕,之前说的学习2之前需要学习3,现在3学完了那么2可以学习了,学完后将visted[2]置为2此时visted=[1,2,2,2]
 *          adjList的第0个元素[1,2]访问完毕,学习0之前需要学习1和2,现在1和2都学完了,将visted[0]置为2此时visted=[2,2,2,2]
 *    然后,对第1门课程进行学习,由于visted[1]已经是2了,说明这个元素已经访问过了,那就退出访问
 *    然后,对第2门课程进行学习,由于visted[2]已经是2了,说明这个元素已经访问过了,那就退出访问
 *    然后,对第3门课程进行学习,由于visted[3]已经是2了,说明这个元素已经访问过了,那就退出访问
 *    遍历结束,不存在环
 */


public class Solution {
   
   
    // 图的邻接表表示
    private List<List<Integer>> adjList;
    // 记录访问状态,0 表示未访问,1 表示正在访问,2 表示已访问完
    private int[] visited;

    public boolean canFinish(int numCourses, int[][] prerequisites) {
   
   
        adjList = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
   
   
            adjList.add(new ArrayList<>());
        }

        visited = new int[numCourses];

        // 构建邻接表
        for (int[] prerequisite : prerequisites) {
   
   
            int course = prerequisite[0];
            int prerequisiteCourse = prerequisite[1];
            adjList.get(prerequisiteCourse).add(course);
        }

        // 对每一门课程进行 DFS 遍历
        for (int i = 0; i < numCourses; i++) {
   
   
            if (!dfs(i)) {
   
   
                return false; // 存在环,返回 false
            }
        }

        return true; // 不存在环,返回 true
    }

    // DFS 遍历函数
    private boolean dfs(int course) {
   
   
        if (visited[course] == 1) {
   
   
            // 发现环,正在访问的节点被再次访问
            return false;
        }
        if (visited[course] == 2) {
   
   
            // 已经访问完的节点,直接返回 true
            return true;
        }

        // 标记为正在访问
        visited[course] = 1;

        // 递归访问所有邻接节点
        for (int nextCourse : adjList.get(course)) {
   
   
            if (!dfs(nextCourse)) {
   
   
                return false;
            }
        }

        // 标记为已访问完
        visited[course] = 2;

        return true;
    }

    public static void main(String[] args) {
   
   
        Solution cs = new Solution();

        int numCourses = 4;
        int[][] prerequisites = {
   
   
                {
   
   1, 0},
                {
   
   2, 0},
                {
   
   3, 1},
                {
   
   3, 2}
        };

        // 构建课程依赖关系图
        // 0 -> 1, 0 -> 2, 1 -> 3, 2 -> 3
        // 表示学习课程 1 前要先学课程 0,学习课程 2 前要先学课程 0,学习课程 3 前要先学课程 1 和 2

        boolean canComplete = cs.canFinish(numCourses, prerequisites);
        System.out.println("是否可以完成所有课程的学习: " + canComplete);
    }
}

Solution208. 实现 Trie (前缀树)

这里是引用

示例:

输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]

解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // 返回 True
trie.search("app");     // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app");     // 返回 True
1、初始化 Trie:
root

2、插入 "apple":
root
 |
 a
 |
 p
 |
 p
 |
 l
 |
 e (isEnd = true)


3、插入 "app":
root
 |
 a
 |
 p
 |
 p (isEnd = true)
 |
 l
 |
 e (isEnd = true)
class TrieNode {
   
   
    // 子节点数组,存储26个小写字母
    private TrieNode[] children;
    // 标记当前节点是否是一个单词的结尾
    private boolean isEnd;

    public TrieNode() {
   
   
        children = new TrieNode[26]; // 初始化子节点数组
        isEnd = false; // 默认不是单词的结尾
    }

    // 检查当前节点是否包含某个字符的子节点
    public boolean containsKey(char ch) {
   
   
        return children[ch - 'a'] != null;
    }

    // 获取某个字符对应的子节点
    public TrieNode get(char ch) {
   
   
        return children[ch - 'a'];
    }

    // 插入一个字符的子节点
    public void put(char ch, TrieNode node) {
   
   
        children[ch - 'a'] = node;
    }

    // 设置当前节点为单词的结尾
    public void setEnd() {
   
   
        isEnd = true;
    }

    // 检查当前节点是否是单词的结尾
    public boolean isEnd() {
   
   
        return isEnd;
    }
}

class Trie {
   
   
    private TrieNode root; // 根节点

    // 初始化前缀树
    public Trie() {
   
   
        root = new TrieNode();
    }

    // 插入一个单词到前缀树中
    public void insert(String word) {
   
   
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
   
   
            char currentChar = word.charAt(i);
            if 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BlackTurn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值