LeetCode刷题之DFS算法

LeetCode刷题之DFS算法

1.基本思路及代码框架

使用DFS或BFS算法遍历二维数组

在二维矩阵中使用DFS搜索,就是把二维矩阵中的每一个位置看成一个节点,这个节点的上下左右四个位置就是相邻节点,那么整个可以抽象为一幅网状的图结构。

根据数据结构和算法思维的框架,根据二叉树的遍历框架写出二维矩阵的DFS代码框架:

//二叉树的遍历框架
void traverse(TreeNode *root) {
    //遍历本节点,前序遍历
    traverse(roo->left);
    //遍历本节点,中序遍历
    traverse(roo->right);
    //遍历本节点,后序遍历
}

//二维矩阵遍历框架
void dfs(vector<vector<int>> &grid, int i, int j, vector<bool> &visited) {
    int m = grid.size(), n = grid[0].size();
    
    //判断节点是否越界
    if (i < 0 || j < 0 || i >= m || j >= n) {
        return;
    }
    //判断是否访问过
    if (visited[i][j]) {
        return;
    }
    //访问节点(i,j)
    visited[i][j] = true;
    dfs(grid, i - 1, j);//上
    dfs(grid, i + 1, j);//下
    dfs(grid, i, j - 1);//左
    dfs(grid, i, j + 1);//右
}

因为二维数组本质是一个图,所以使用visited布尔数组可以避免走回头路。

这⾥额外说⼀个处理⼆维数组的常⽤⼩技巧,你有时会看到使⽤「⽅向数组」来处理上下左右的遍历,和前
⽂ 图遍历框架 的代码很类似:

// ⽅向数组,分别代表上、下、左、右
int[][] dirs = new int[][]{{-1,0}, {1,0}, {0,-1}, {0,1}};
void dfs(int[][] grid, int i, int j, boolean[] visited) {
    int m = grid.length, n = grid[0].length;
    if (i < 0 || j < 0 || i >= m || j >= n) {
        // 超出索引边界
        return;
    }
    if (visited[i][j]) {
        // 已遍历过 (i, j)
        return;
    }
    // 进⼊节点 (i, j)
    visited[i][j] = true;
    // 递归遍历上下左右的节点
    for (int[] d : dirs) {
        int next_i = i + d[0];
        int next_j = j + d[1];
        dfs(grid, next_i, next_j);
    }
    // 离开节点 (i, j)
}

2.题目解答

1.LeetCode之200岛屿数量问题

  • 解题思路

    使用深度优先搜索,将每一个岛屿都淹没,避免维护visited数组。

  • 代码实现

    /*
     * @lc app=leetcode.cn id=200 lang=cpp
     *
     * [200] 岛屿数量
     */
    
    // @lc code=start
    class Solution {
    public:
        int numIslands(vector<vector<char>>& grid) {
            int res = 0;
            int m = grid.size(), n = grid[0].size();
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == '1') {
                        ++res;
                        dfs(grid, i, j);//将与此块连接的块全部淹没
                    }
                }
            }
            return res;
        }
    
        void dfs(vector<vector<char>> &grid, int i, int j) {
            int m = grid.size(), n = grid[0].size();
            
            if (i < 0 || j < 0 || i >= m || j >= n) {
                return;
            }
    
            if (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);
        }
    };
    // @lc code=end
    

2.LeetCode之1254统计封闭岛屿的数量

  • 解题思路

    • 与上面思路一致,不过本题不同的是周围靠边的不算岛屿,所以先要将四周的岛屿进行淹没,然后再进行深度遍历。
    • 除了使用DFS/BFS算法以外,还可以使用Union Find算法运用(后续学习在实现)。
  • 代码实现

    /*
     * @lc app=leetcode.cn id=1254 lang=cpp
     *
     * [1254] 统计封闭岛屿的数目
     */
    
    // @lc code=start
    class Solution {
    public:
        int closedIsland(vector<vector<int>>& grid) {
            int m = grid.size(), n = grid[0].size();
            for (int i = 0; i < n; i++) {
                dfs(grid, 0, i);
                dfs(grid, m - 1, i);
            }
    
            for (int j = 0; j < m; j++) {
                dfs(grid, j, 0);
                dfs(grid, j, n - 1);
            }
            int res = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++){
                    if (grid[i][j] == 0) {
                        ++res;
                        dfs(grid, i, j);
                    }
                }
            }
            return res;
        }
    
        void dfs(vector<vector<int>> &grid, int i, int j) {
            int m = grid.size(), n = grid[0].size();
            if (i < 0 || j < 0 || i >= m || j >= n) {
                return;
            }
    
            if (grid[i][j] == 1) {
                return;
            }
    
            grid[i][j] = 1;
            dfs(grid, i - 1, j);
            dfs(grid, i + 1, j);
            dfs(grid, i, j - 1);
            dfs(grid, i, j + 1);
        }
    };
    // @lc code=end
    

3.LeetCode之1020飞地的数量

  • 解题思路

    与上面思路一致,将周围的岛屿淹没后,统计剩下陆地块的数量即为所求。

  • 代码实现

    /*
     * @lc app=leetcode.cn id=1020 lang=cpp
     *
     * [1020] 飞地的数量
     */
    
    // @lc code=start
    class Solution {
    public:
        int numEnclaves(vector<vector<int>>& grid) {
             int m = grid.size(), n = grid[0].size();
            for (int i = 0; i < n; i++) {
                dfs(grid, 0, i);
                dfs(grid, m - 1, i);
            }
    
            for (int j = 0; j < m; j++) {
                dfs(grid, j, 0);
                dfs(grid, j, n - 1);
            }
            
            int res = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == 1) {
                        res += 1;
                    }
                }
            }
            return res;
        }
    
        void dfs(vector<vector<int>> &grid, int i, int j) {
            int m = grid.size(), n = grid[0].size();
            
            if (i < 0 || j < 0 || i >= m || j >=n) {
                return;
            }
    
            if (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);
        }
    };
    // @lc code=end
    

4.LeetCode之695岛屿的最大面积

  • 解题思路

    遍历岛屿,计算每个岛屿的面积,返回最大的面积。

  • 代码实现

    /*
     * @lc app=leetcode.cn id=695 lang=cpp
     *
     * [695] 岛屿的最大面积
     */
    
    // @lc code=start
    class Solution {
    public:
        int maxAreaOfIsland(vector<vector<int>>& grid) {
            int m = grid.size(), n = grid[0].size();
    
            int maxSize = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0 ; j < n; j++) {
                    int s = 0;
                    if (grid[i][j] == 1) {
                        dfs(grid, i, j, s);
                        maxSize = max(maxSize, s);
                    }
                }
            }
            return maxSize;
        }
    
        void dfs(vector<vector<int>> &grid, int i, int j, int &s) {
            int m = grid.size(), n = grid[0].size();
            
            if (i < 0 || j < 0 || i >= m || j >=n) {
                return;
            }
    
            if (grid[i][j] == 0) {
                return;
            }
    
            grid[i][j] = 0;
            s = s + 1;
            dfs(grid, i - 1, j, s);
            dfs(grid, i + 1, j, s);
            dfs(grid, i, j - 1, s);
            dfs(grid, i, j + 1, s);
        }
    };
    // @lc code=end
    

    5.LeetCode之1905统计子岛屿

  • 解题思路

    如果岛屿2中有的陆地而岛屿1中对应的位置为水,说明这个陆地所处的岛屿肯定不是子岛屿,可直接将其淹没。剩下的岛屿都为子岛屿。

  • 代码实现

    /*
     * @lc app=leetcode.cn id=1905 lang=cpp
     *
     * [1905] 统计子岛屿
     */
    
    // @lc code=start
    class Solution {
    public:
        int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
             int m = grid2.size(), n = grid2[0].size();
             for (int i = 0; i < m; i++) {
                 for (int j = 0; j < n; j++) {
                     if (grid1[i][j] == 0 && grid2[i][j] == 1) {
                         dfs(grid2, i ,j);
                     }
                 }
             }
             int res = 0;
             for (int i = 0; i < m; i++) {
                 for (int j = 0; j < n; j++) {
                     if (grid2[i][j] == 1) {
                         ++res;
                         dfs(grid2, i, j);
                     }
                 }
             }
             return res;
        }
    
    
       void dfs(vector<vector<int>> &grid, int i, int j) {
            int m = grid.size(), n = grid[0].size();
            if (i < 0 || j < 0 || i >= m || j >= n) {
                return;
            }
    
            if (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);
        }
    };
    // @lc code=end
    

6.LeetCode之694不同的岛屿数量

  • 看完图的遍历和二叉树的序列化和反序列化再来写。
posted @ 2021-11-24 12:43  IU_UI  阅读(553)  评论(0编辑  收藏  举报