hot100-图论/岛屿问题

问题模板

为什么要将格子标记为【已遍历】---避免 重复遍历

(这是因为,网格结构本质上是一个「图」,我们可以把每个格子看成图中的结点,每个结点有向上下左右的四条边。在图中遍历时,自然可能遇到重复遍历结点。这时候,DFS 可能会不停地「兜圈子」,永远停不下来

java 复制代码
void dfs(int[][] grid, int r, int c) {
    // 判断 base case
    if (!inArea(grid, r, c)) {
        return;
    }
    // 如果这个格子不是岛屿,直接返回
    if (grid[r][c] != 1) {
        return;
    }
    grid[r][c] = 2; // 将格子标记为「已遍历过」
    
    // 访问上、下、左、右四个相邻结点
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
}

// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
    return 0 <= r && r < grid.length 
        	&& 0 <= c && c < grid[0].length;
}

200. 岛屿数量

java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        if(grid==null||grid.length==0)
            return 0;
        int cnt=0;
        int m=grid.length;
        int n =grid[0].length;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]=='1')
                {
                    cnt++;
                    dfs(grid,i,j);
                }
            }
        }
        return cnt;
    }

    void dfs(char[][] grid, int r, int c) {
    // 判断 base case
    if (!inArea(grid, r, c)) {
        return;
    }
    // 如果这个格子不是岛屿,直接返回
    if (grid[r][c] != '1') {
        return;
    }
    grid[r][c] = '2'; // 将格子标记为「已遍历过」
    
    // 访问上、下、左、右四个相邻结点
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
    }

    // 判断坐标 (r, c) 是否在网格中
    boolean inArea(char[][] grid, int r, int c) {
        return 0 <= r && r < grid.length 
                && 0 <= c && c < grid[0].length;
    }
}

695. 岛屿的最大面积

这道题同样根据dfs模板走,只不过区别是dfs返回值变成了 1+dfs(上下左右)的面积

java 复制代码
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int max_area=0;
        for(int i = 0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                int temp_area=dfs(grid,i,j);
                max_area=Math.max(max_area,temp_area);
            }
        }
        return max_area;

    }
    int  dfs(int[][] grid,int r,int c)
    {
        int m = grid.length;
        int n= grid[0].length;
        if(!inArea(grid,r,c))
            return 0;
        //不是岛屿 直接返回
        if(grid[r][c]!=1)
            return 0;
        grid[r][c]=2;
        return 1+
        dfs(grid,r-1,c)
        +dfs(grid,r+1,c)
        +dfs(grid,r,c-1)
        +dfs(grid,r,c+1);


    }
    boolean inArea(int[][] grid,int r,int c)
    {
        return r>=0&&r<grid.length&&c>=0&&c<grid[0].length;
    }
}

463. 岛屿的周长

岛屿的周长是计算岛屿全部的「边缘」,而这些边缘就是我们在 DFS 遍历中,dfs 函数返回的位置。

当我们的 dfs 函数因为「坐标 (r, c) 超出网格范围」返回的时候,实际上就经过了一条黄色的边;而当函数因为「当前格子是海洋格子」返回的时候,实际上就经过了一条蓝色的边

java 复制代码
class Solution {
    public int islandPerimeter(int[][] grid) {
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                if (grid[r][c] == 1) {
                    // 题目限制只有一个岛屿,计算一个即可
                    return dfs(grid, r, c);
                }
            }
        }
        return 0;
    }

    int dfs(int[][] grid, int r, int c) {
        // 函数因为「坐标 (r, c) 超出网格范围」返回,对应一条黄色的边
        if (!inArea(grid, r, c)) {
            return 1;
        }
        // 函数因为「当前格子是海洋格子」返回,对应一条蓝色的边
        if (grid[r][c] == 0) {
            return 1;
        }
        // 函数因为「当前格子是已遍历的陆地格子」返回,和周长没关系
        if (grid[r][c] != 1) {
            return 0;
        }
        grid[r][c] = 2;
        return dfs(grid, r - 1, c)
            + dfs(grid, r + 1, c)
            + dfs(grid, r, c - 1)
            + dfs(grid, r, c + 1);
    }

    // 判断坐标 (r, c) 是否在网格中
    boolean inArea(int[][] grid, int r, int c) {
        return 0 <= r && r < grid.length 
                && 0 <= c && c < grid[0].length;
    }
}

另一类bfs问题

994. 腐烂的橘子

想象以污染的橘子为污染源,然后将相邻的橘子一层一层污染,考虑BFS

java 复制代码
class Solution{
public int orangesRotting(int[][] grid) {
    int M = grid.length;
    int N = grid[0].length;
    Queue<int[]> queue = new LinkedList<>();

    int count = 0; // count 表示新鲜橘子的数量
    for (int r = 0; r < M; r++) {
        for (int c = 0; c < N; c++) {
            if (grid[r][c] == 1) {
                count++;
            } 
            //一开始,我们找出所有腐烂的橘子,将它们放入队列,作为第 0 层的结点。
            //就是污染源
            else if (grid[r][c] == 2) {
                queue.add(new int[]{r, c});
            }
        }
    }

    int round = 0; // round 表示腐烂的轮数,或者分钟数
    while (count > 0 && !queue.isEmpty()) {
        round++;
        int n = queue.size();
        for (int i = 0; i < n; i++) {
            int[] orange = queue.poll();
            int r = orange[0];
            int c = orange[1];
            //将相邻方向依次污染
            if (r-1 >= 0 && grid[r-1][c] == 1) {
                grid[r-1][c] = 2;
                count--;
                queue.add(new int[]{r-1, c});
            }
            if (r+1 < M && grid[r+1][c] == 1) {
                grid[r+1][c] = 2;
                count--;
                queue.add(new int[]{r+1, c});
            }
            if (c-1 >= 0 && grid[r][c-1] == 1) {
                grid[r][c-1] = 2;
                count--;
                queue.add(new int[]{r, c-1});
            }
            if (c+1 < N && grid[r][c+1] == 1) {
                grid[r][c+1] = 2;
                count--;
                queue.add(new int[]{r, c+1});
            }
        }
    }

    if (count > 0) {
        return -1;
    } else {
        return round;
    }
}
}

207. 课程表

java 复制代码
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
         //判断是否是有向无环图-------》拓扑排序
        //通过课程前置条件列表 prerequisites 可以得到课程安排图的 邻接表 adjacency
        //统计课程安排图中每个节点的入度,生成 入度表 indegrees。
        //借助一个队列 queue,将所有入度为 0 的节点入队。
        
        //拓扑排序过程
        //删顶点 再把顶点的出边删掉,即对应所有邻接节点的入度-1
        //当入度 −1后邻接节点 cur 的入度为 0,说明 cur 所有的前驱节点已经被 "删除",
        //此时将 cur 入队。

        int[] indegree = new int[numCourses];
        List<List<Integer>> adjacent  = new ArrayList<>();
        Queue<Integer> list = new LinkedList<>();
        for(int i=0;i<numCourses;i++)
            adjacent.add(new ArrayList<>());
        for(int[] tmp : prerequisites) {
            indegree[tmp[0]]++;
            adjacent.get(tmp[1]).add(tmp[0]);
        }
        //将所以入度为0的节点入队
        for(int i=0;i<numCourses;i++)
        {
            if(indegree[i]==0)
                list.add(i);
        }
        //排序过程
        while(!list.isEmpty())
        {
            int pre =list.poll();
            numCourses--;
            //他的邻接节点入度--
            for(int tmp :adjacent.get(pre))
            {
                indegree[tmp]--;
                if(indegree[tmp]==0)
                    list.add(tmp);
            }
        }
        return numCourses==0;


    }
}

208. 实现 Trie (前缀树)

java 复制代码
class Trie {
    class TrieNode{
        private boolean isEnd;
        TrieNode[] next;
        public TrieNode() {
            isEnd = false;
            next= new TrieNode[26];
        }
    }

    private TrieNode root;
    public Trie()
    {
        root = new TrieNode();
    }
    public void insert(String word) {
        TrieNode node = root;
        for(char c : word.toCharArray())
        {
            if(node.next[c-'a']==null)
                node.next[c-'a']=new TrieNode();
            node=node.next[c-'a'];
        }
        node.isEnd = true;
    }


    public boolean search(String word) {
        TrieNode node = root;
        for(char c :word.toCharArray())
        {
            if(node.next[c-'a']==null)
            {
                return false;
            }
            node=node.next[c-'a'];
            
        }
        return node.isEnd;

    }
    
    public boolean startsWith(String prefix) {
         TrieNode node = root;
        for(char c :prefix.toCharArray())
        {
            if(node.next[c-'a']==null)
            {
                return false;
            }
            node=node.next[c-'a'];
            
        }
        return true;

    }
}

注意 startwith和search的区别

startwith 走完prefix循环就可以返回true;

而search走完循环还要看是不是到最终的叶结点了

不然 比如word为prefix 而树中是prefixxx 也会错误的search为true

相关推荐
no_play_no_games4 小时前
「3.3」虫洞 Wormholes
数据结构·c++·算法·图论
Zhang_xiangyou9 小时前
【图论】1 (最小生成树&虚拟点思想)C.戴森球计划 题解
c++·学习·算法·图论
叫我Cow_14 小时前
全排列和组合数区分
算法·深度优先·图论
FindYou.15 小时前
D - Hidden Weights
c++·算法·图论
修炼室2 天前
深度优先搜索(DFS)解题思路解析:如何通过图论计算除法求值
算法·深度优先·图论
larryyu_cs2 天前
CF687D Dividing Kingdom II 题解
数据结构·c++·算法·图论
良月澪二2 天前
CSP-S 2022 T1假期计划
算法·图论
抓哇能手2 天前
王道408考研数据结构-图-第六章
c语言·数据结构·考研·算法·图论·408
LluckyYH2 天前
代码随想录Day 59|图论Part09,dijkstra(堆优化版)精讲、Bellman_ford算法精讲
数据结构·人工智能·算法·深度优先·图论
Kkkuu..2 天前
代码随想录Day17 图论-3
数据结构·c++·算法·图论