LeetCode刷题笔记之图论

1. 797【所有可能的路径】

  • 题目: 给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)。graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。
  • 代码:
class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new LinkedList<>();
    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        //深度优先遍历,本质上与回溯算法一样
        //先使用for循环进行遍历每个节点的下一个可到达节点
        //然后在for循环里进行递归遍历每条路径
        //终止条件是到达最后一个节点
        path.add(0);
        dfs(graph,0);
        return ans;
    }
    public void dfs(int[][] graph,int index){
        if(index == graph.length-1){
            ans.add(new ArrayList(path));
            return;
        }
        for(int i=0;i<graph[index].length;i++){
            path.add(graph[index][i]);
            dfs(graph,graph[index][i]);
            path.removeLast();
        }
    }
}

2. 200【岛屿数量】

  • 题目: 给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。
  • 代码:
class Solution {
    public int numIslands(char[][] grid) {
        //DFS,设置一个数组记录是否被访问过
        //遍历到一个陆地就进行dfs,land数量加1
        int num = 0;
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] isVisited = new boolean[n][m];
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!isVisited[i][j] && grid[i][j]=='1'){
                    num++;
                    dfs(grid,isVisited,i,j);
                }
            }
        }
        return num;
    }
    public void dfs(char[][] grid,boolean[][] isVisited,int x,int y){
        if(x<0 || x>=grid.length || y<0 || y>=grid[0].length){
            return;
        }
        if(isVisited[x][y] || grid[x][y]=='0'){
            return;
        }
        isVisited[x][y] = true;
        int[][] loc = new int[][]{
            {-1,0},
            {1,0},
            {0,-1},
            {0,1}
        };
        for(int i=0;i<4;i++){
            int xTmp = x+loc[i][0];
            int yTmp = y+loc[i][1];
            dfs(grid,isVisited,xTmp,yTmp);
        }
    }
}

3. 695【岛屿的最大面积】

  • 题目: 给你一个大小为 m x n 的二进制矩阵 grid 。
    岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。岛屿的面积是岛上值为 1 的单元格的数目。计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
  • 代码:
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        //先用dfs找到岛屿,计算每个岛屿的面积
        //记录最大值
        int m = grid.length;
        int n = grid[0].length;
        boolean[][] isVisited = new boolean[m][n];
        int max = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(!isVisited[i][j] && grid[i][j]==1){
                    int tmp = dfs(grid,isVisited,i,j);
                    max = Math.max(tmp,max);
                }
            }
        }
        return max;
    }
    public int dfs(int[][] grid,boolean[][] isVisited,int x,int y){
        if(isVisited[x][y] || grid[x][y]==0){
            return 0;
        }
        isVisited[x][y] = true;
        int[][] loc = new int[][]{
            {0,1},
            {0,-1},
            {1,0},
            {-1,0}
        };
        int area = 1;
        for(int i=0;i<4;i++){
            int tmpX = x + loc[i][0];
            int tmpY = y + loc[i][1];
            if(tmpX<0 || tmpX>=grid.length || tmpY<0 || tmpY>=grid[0].length){
                continue;
            }
            area += dfs(grid,isVisited,tmpX,tmpY);
        }
        
        return area;
    }
}

4. 1020【飞地的数量】

  • 题目: 给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。
  • 代码:
class Solution {
    int num = 0;
    int[][] loc = new int[][]{
        {0,1},
        {0,-1},
        {1,0},
        {-1,0}
    };
    public int numEnclaves(int[][] grid) {
        //首先使用dfs标记所有边界位置的陆地,
        //然后再遍历内部陆地,同时记录个数
        int m = grid.length;
        int n = grid[0].length;
        boolean[][] isVisited = new boolean[m][n];
        for(int i=0;i<m;i++){
            if(!isVisited[i][0] && grid[i][0]==1) dfs(grid,isVisited,i,0);
            if(!isVisited[i][n-1] && grid[i][n-1]==1) dfs(grid,isVisited,i,n-1);
        }
        for(int i=0;i<n;i++){
            if(!isVisited[0][i] && grid[0][i]==1) dfs(grid,isVisited,0,i);
            if(!isVisited[m-1][i] && grid[m-1][i]==1) dfs(grid,isVisited,m-1,i);
        }
        num = 0;
        for(int i=1;i<m;i++){
            for(int j=0;j<n;j++){
                if(!isVisited[i][j] && grid[i][j]==1){
                    dfs(grid,isVisited,i,j);
                }
            }
        }
        return num;
    }
    public void dfs(int[][] grid,boolean[][] isVisited,int x,int y){
        if(grid[x][y]==0 || isVisited[x][y] ){
            return;
        }
        isVisited[x][y] = true;
        num++;
        
        for(int i=0;i<4;i++){
            int tmpX = x + loc[i][0];
            int tmpY = y + loc[i][1];
            if(tmpX<0 || tmpX>=grid.length || tmpY<0 || tmpY>=grid[0].length){
                continue;
            }
            dfs(grid,isVisited,tmpX,tmpY);
        }
    }
}

5. 130【被围绕的区域】

  • 题目: 给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

  • 代码:

class Solution {
    public void solve(char[][] board) {
        //使用visited数组标注已经访问过的位置
        //先遍历边缘位置,然后对内部未访问过的‘O’替换
        int n = board.length;
        int m = board[0].length;
        boolean[][] visited = new boolean[n][m];
        for(int i=0;i<n;i++){
            if(board[i][0]=='O') dfs(board,i,0,visited);
            if(board[i][m-1]=='O') dfs(board,i,m-1,visited);
        }
        for(int i=0;i<m;i++){
            if(board[0][i]=='O') dfs(board,0,i,visited);
            if(board[n-1][i]=='O') dfs(board,n-1,i,visited);
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!visited[i][j] && board[i][j]=='O'){
                    board[i][j]='X';
                }
            }
        }
    }
    public void dfs(char[][] board,int x,int y,boolean[][] visited){
        if(x<0|| x>=board.length||y<0||y>=board[0].length||visited[x][y]==true){
            return;
        }
        if(board[x][y]=='X') return;
        visited[x][y] = true;
        int[][] loc = new int[][]{
            {1,0},{-1,0},{0,1},{0,-1}
        };
        for(int i=0;i<4;i++){
            int tmpX=x+loc[i][0];
            int tmpY=y+loc[i][1];
            dfs(board,tmpX,tmpY,visited);
        }
    }
}

6. 417【太平洋大西洋水流问题】

  • 题目: 有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度 。岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动 既可流向太平洋也可流向大西洋 。
  • 代码:
class Solution {
    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        //题目的意思就是找到既能通往太平洋,又能通往大西洋的节点
        //从太平洋一侧遍历找到可以流到太平洋的所有节点
        //从大西洋异常遍历找到可以流到大西洋的所有节点
        //这两次遍历的公共节点即为既能通往太平洋,又能通往大西洋的节点
        int n = heights.length;
        int m = heights[0].length;
        boolean[][][] visited = new boolean[n][m][2];
        for(int i=0;i<n;i++){
            dfs(heights,i,0,visited,0);
            dfs(heights,i,m-1,visited,1);
        }
        for(int i=0;i<m;i++){
            dfs(heights,0,i,visited,0);
            dfs(heights,n-1,i,visited,1);
        }
        List<List<Integer>> ans = new ArrayList<>();
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(visited[i][j][0] && visited[i][j][1]){
                    List<Integer> tmp = new LinkedList<>();
                    tmp.add(i);
                    tmp.add(j);
                    ans.add(tmp);
                }
            }
        }
        return ans;
    }
    public void dfs(int[][] heights,int x,int y,boolean[][][] visited,int dst){
        if(visited[x][y][dst]) return;
        visited[x][y][dst] = true;
        int[][] loc = new int[][]{
            {1,0},{-1,0},{0,1},{0,-1}
        };
        for(int i=0;i<4;i++){
            int tmpX=x+loc[i][0];
            int tmpY=y+loc[i][1];
            if(tmpX<0|| tmpX>=heights.length||tmpY<0||tmpY>=heights[0].length){
                continue;
            }
            if(heights[x][y]<=heights[tmpX][tmpY])
                dfs(heights,tmpX,tmpY,visited,dst);
        }
    }
}

7. 841【钥匙和房间】

  • 题目: 有 n 个房间,房间按从 0 到 n - 1 编号。最初,除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而,你不能在没有获得钥匙的时候进入锁住的房间。
    当你进入一个房间,你可能会在里面找到一套不同的钥匙,每把钥匙上都有对应的房间号,即表示钥匙可以打开的房间。你可以拿上所有钥匙去解锁其他房间。
    给你一个数组 rooms 其中 rooms[i] 是你进入 i 号房间可以获得的钥匙集合。如果能进入 所有 房间返回 true,否则返回 false。
  • 代码:
class Solution {
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        //先使用dfs遍历得到所有可以打开的房间,
        //然后判断是否有未打开的房间
        int n = rooms.size();
        boolean[] visited = new boolean[n];
        Arrays.fill(visited,false);
        dfs(rooms,0,visited);
        for(int i=0;i<n;i++){
            if(!visited[i]) return false;
        }
        return true;
    }
    public void dfs(List<List<Integer>> rooms,int x,boolean[] visited){
        if(visited[x]) return;
        visited[x] = true;
        for(int i=0;i<rooms.get(x).size();i++){
            dfs(rooms,rooms.get(x).get(i),visited);
        }
    }
}

8. 463【岛屿的周长】

  • 题目: 给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
    网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
    岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

  • 代码:

class Solution {
    public int islandPerimeter(int[][] grid) {
        //当陆地旁边是边界或水域时,需要计算周长
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] visited = new boolean[n][m];
        int ans=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!visited[i][j] && grid[i][j]==1){
                    ans+= dfs(grid,i,j,visited);
                }
            }
        }
        return ans;
    }
    public int dfs(int[][] grid,int x,int y,boolean[][] visited){
        if(x<0||x>=grid.length||y<0||y>=grid[0].length) return 1;
        if(grid[x][y]==0) return 1;
        if(visited[x][y]) return 0;
        visited[x][y] = true;
        int[][] loc = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
        int ans = 0;
        for(int i=0;i<4;i++){
            int tmpX=x+loc[i][0];
            int tmpY=y+loc[i][1];
            ans+=dfs(grid,tmpX,tmpY,visited);
        }
        return ans;
    }
}

9. 1971【寻找图中是否存在路径】

  • 题目: 有一个具有 n 个顶点的 双向 图,其中每个顶点标记从 0 到 n - 1(包含 0 和 n - 1)。图中的边用一个二维整数数组 edges 表示,其中 edges[i] = [ui, vi] 表示顶点 ui 和顶点 vi 之间的双向边。 每个顶点对由 最多一条 边连接,并且没有顶点存在与自身相连的边。请你确定是否存在从顶点 source 开始,到顶点 destination 结束的 有效路径 。给你数组 edges 和整数 n、source 和 destination,如果从 source 到 destination 存在 有效路径 ,则返回 true,否则返回 false 。
  • 代码:
class Solution {
    public int[] fa;
    public boolean validPath(int n, int[][] edges, int source, int destination) {
        //可以使用并查集
        //首先初始化每个节点的father节点
        //如果两个节点之间有边,就将两个节点合并到一个集合
        fa = new int[n];
        init();
        for(int i=0;i<edges.length;i++){
            union(edges[i][0],edges[i][1]);
        }
        return find(source)==find(destination);
    }
    public void init(){
        for(int i=0;i<fa.length;i++){
            fa[i] = i;
        }
    }
    public int find(int u){
        if(u==fa[u])
            return u;
        else
            fa[u] = find(fa[u]);
        return fa[u];
    }
    public void union(int u,int v){
        u = find(u);
        v = find(v);
        fa[u] = v;
    }
}

10. 684【冗余连接】

  • 题目: 树可以看成是一个连通且无环的无向图。
    给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
    请找出一条可以删去的边,删除后可使得剩余部分是一个有着n个节点的树。如果有多个答案,则返回数组 edges 中最后出现的那个。
  • 代码:
class Solution {
    public int[] fa;
    public int[] findRedundantConnection(int[][] edges) {
        //利用并查集,如果某一条边的两个节点处于相同的集合,
        //那么这条边一定会造成环的出现
        //只需判断第一个出现环的边即可找出结果
        fa = new int[edges.length+1];
        init();
        for(int i=0;i<edges.length;i++){
            if(find(edges[i][0]) == find(edges[i][1]))
                return edges[i];
            else union(edges[i][0],edges[i][1]);
        }
        return new int[2];
    }
    public void init(){
        for(int i=1;i<fa.length;i++){
            fa[i] = i;
        }
    }
    public int find(int u){
        if(u==fa[u]) return u;
        else fa[u] = find(fa[u]);
        return fa[u];
    }
    public void union(int u,int v){
        u = find(u);
        v = find(v);
        fa[u] = v;
    }

}
  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值