Hot100中的:图论专题

图模板

  • 图分为有向图和无向图,入度是指向当前节点的边数,出度是当前节点指向其他节点的边数

200.岛屿数量

关键信息一句话总结:

遍历网格,遇到陆地就用 DFS / BFS 把整块连通陆地淹掉,并计数

  • 方法1:BFS
java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        // bfs是这样的 联想我们之前的二叉树
        // bfs一层一层是左右节点延申出去的
        // 而这里的图是上下左右四个方向表示一层,所以bfs可以一层一层
        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') {
                    bfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }  
    private void bfs(char[][] grid, int i, int j) {
        int m = grid.length;
        int n = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{i, j});
        grid[i][j] = '0';
        while(!queue.isEmpty()) {
            int[] cur = queue.poll();
            int x = cur[0];
            int y = cur[1];
            int[][] dirs = {{1, 0}, {-1 ,0}, {0, 1}, {0, -1}};
            for(int[] d : dirs) {
                int nx = x + d[0];
                int ny = y + d[1];
                if(nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == '1') {
                    queue.offer(new int[]{nx, ny});
                    grid[nx][ny] = '0';
                }
            }
        }
    }
}
  • 方法2:DFS
java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        int count = 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') {
                    dfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    private void dfs(char[][] grid, int i, int j) {
        int m = grid.length;
        int n = grid[0].length;
        if(i >= m || i < 0 || j >= n || j < 0 || grid[i][j] == '0') {
            return;
        }
        // 把当前陆地淹没
        grid[i][j] = '0';
        // 四个方向搜索
        dfs(grid, i - 1, j);
        dfs(grid, i, j + 1);
        dfs(grid, i + 1, j);
        dfs(grid, i, j - 1);
    }
}

方法3:并查集

1.什么是并查集

并查集 = 用来判断两个东西是不是在同一个集合里,它解决了判断这两个点是不是连在一起的问题

java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        UnionFind uf = new UnionFind(m * n);
        int count = 0;
        // 1.统计所有'1'
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == '1') {
                    count++;
                }
            }
        }
        // 2.合并相邻的'1'
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == '1') {
                    // 有i行 每行n个 所以 i * n 在j列 所以 i * n + j 二维变一维
                    int index = i * n + j;
                    // 向下和向右已经包括了所有的覆盖情况 因为我们是从高到低 从左到右遍历的 联想八皇后!
                    // 向下
                    if(i + 1 < m && grid[i + 1][j] == '1') {
                        // 将这个index所在的数 与它下面一个数进行合并
                        if(uf.union(index, (i + 1) * n + j)) {
                            count--;
                        }
                    }
                    // 向右
                    if(j + 1 < n && grid[i][j + 1] == '1') {
                        if(uf.union(index, i * n + j + 1)) {
                            count--;
                        }
                    }
                }
            }
        }
        return count;
    }
    class UnionFind {
        // parent的index表示本身的值 value表示指向父集合 其实就是 index 最终属于 value
        int[] parent;

        public UnionFind(int n) {
            parent = new int[n];
            for(int i = 0; i < n; i++) {
                parent[i] = i;
            }
        }
        // 找到最终的父集合并且把路径全部指向最终的父集合
        public int find(int x) {
            if(parent[x] != x) {
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }   
        // 合并两个集合
        public boolean union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if(rootX == rootY) {
                return false;
            }
            parent[rootX] = parent[rootY];
            return true;
        }
    }
}
  • 反思:我没有想到图的遍历需要用dfs和bfs进行搜索,连通性问题、集合合并问题可以用并查集

994.腐烂的橘子

关键信息一句话总结:

多源 BFS,扩散一层就是一分钟

java 复制代码
class Solution {
    public int orangesRotting(int[][] grid) {
        // 多源bfs 区别于岛屿对时间无要求 每个都bfs就好了
        int count = -1;
        int m = grid.length;
        int n = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();
        int fresh = 0;
        for(int i = 0 ; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == 2) {
                    queue.offer(new int[]{i ,j});
                }
                if(grid[i][j] == 1) {
                    fresh++;
                }
            }
        }
        if(fresh == 0) {
            return 0;
        }
        while(!queue.isEmpty()) {
            int size = queue.size();
            // 每一层算1分钟 刚开始不算 从0分钟开始
            count++;
            for(int i = 0; i < size; i++) {
                int[] cur = queue.poll();
                int x = cur[0];
                int y = cur[1];
                int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
                for(int[] d : dirs) {
                    int nx = x + d[0];
                    int ny = y + d[1];
                    if(nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {
                        grid[nx][ny] = 2;
                        fresh--;
                        queue.offer(new int[]{nx, ny});
                    }
                }
            }
        }
        if(fresh > 0) {
            return -1;
        }
        return count;
    }
   
}
  • 反思:我没有抓住它对时间的要求,需要同时多源进行bfs,而200题不需要多源进行,只需要延申就行了

207.课程表

关键信息一句话总结:

用拓扑排序判断有向图中是否存在环

java 复制代码
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        // 本质:判断有向图是否存在环 
        // 1.建图
        List<List<Integer>> graph = new ArrayList<>();
        for(int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<>());
        }
        // 2.入度数组
        int[] indegree = new int[numCourses];
        // 3.构建邻接表和入度
        for(int[] p : prerequisites) {
            int a = p[0];
            int b = p[1];
            // b这个节点是a的前置
            graph.get(b).add(a);
            // a的前置(入度: 指向该节点的边的数量) +1 
            indegree[a]++;
        }
        // 4.把所有入度为0的课程入队
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 0; i < numCourses; i++) {
            if(indegree[i] == 0) {
                queue.offer(i);
            }
        }
        // 5.拓扑排序
        int count = 0;
        while(!queue.isEmpty()) {
            int cur = queue.poll();
            count++;
            for(int next : graph.get(cur)) {
                indegree[next]--;
                if(indegree[next] == 0) {
                    queue.offer(next);
                }
            }
        }
        return count == numCourses;
    }
}
  • 反思:我没有建模成功,建模成判断图中是否存在环,应该用拓扑排序

208.实现Trie前缀树

关键信息一句话总结:

把字符串拆成路径存进树里,用共享前缀实现高效查找与前缀匹配

  • 分析:isEnd用来标记是否是单词的结尾,因为app和apple有共享前缀
java 复制代码
class Trie {

    class Node {
        Node[] children = new Node[26];
        boolean isEnd;
    }
    
    private Node root;
    
    public Trie() {
        root = new Node();
    }
    
    public void insert(String word) {
        Node node = root;
        for(char c : word.toCharArray()) {
            int index = c - 'a';
            // 只要 new 过一次 这个 if 以后永远是 false
            if(node.children[index] == null) {
                node.children[index] = new Node();
            }
            node = node.children[index];
        }
        node.isEnd = true;
    }
    
    public boolean search(String word) {
        Node node = find(word);
        if(node != null && node.isEnd) {
            return true;
        }
        return false;
    }
    
    public boolean startsWith(String prefix) {
        if(find(prefix) != null) {
            return true;
        }
        return false;
    }

    private Node find(String s) {
        Node node = root;
        for(char c : s.toCharArray()) {
            int index = c - 'a';
            if(node.children[index] == null) {
                return null;
            }
            node = node.children[index];
        }
        return node;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */
  • 反思:我没有想到共享前缀这个关键点,我认为用一个数组或者链表就能完成,然而要保证前缀,所以有共享前缀,不同分支,所以就有了链表 + 哈希表
相关推荐
不染尘.9 小时前
最小生成树算法
开发语言·数据结构·c++·算法·图论
I_LPL10 小时前
day58 代码随想录算法训练营 图论专题11
数据结构·算法·图论
重生之后端学习1 天前
287. 寻找重复数
数据结构·算法·leetcode·深度优先·图论
I_LPL1 天前
day57 代码随想录算法训练营 图论专题10
图论
jing-ya2 天前
day 55 图论part7
java·数据结构·算法·图论
技术净胜2 天前
Gephi基于图论与物理模拟的网络可视化原理
数据分析·图论
落地加湿器2 天前
Acwing算法课图论与搜索笔记
c++·笔记·算法·图论·dfs·bfs·图搜索算法
ccLianLian2 天前
图论·二分图
图论
ccLianLian2 天前
图论·图的存储
图论