LeetCode刷题--DFS与BFS --岛屿的最大面积与地图分析

    题目均来自于力扣,最近力扣正在举行每日一题的活动,有兴趣的小伙伴可以多参与哦。

    先来看看岛屿的最大面积的题目描述吧。

 

给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

 

示例 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1 。

示例 2:

[[0,0,0,0,0,0,0,0]]
对于上面这个给定的矩阵, 返回 0。

注意: 给定的矩阵grid 的长度和宽度都不超过 50。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-area-of-island

  翻译一下题目,就是在二维矩阵中寻找相邻皆为1的最大面积。注意这里的相邻指的是row索引差值为1或是column索引差值为1。

  笔者从最朴素的想法开始,双重循环遍历整个矩阵,向右向下寻找,如果遇到相邻的陆地就加入,从而在O(n)的时间内计算出结果。但是仔细思考一下就能发现这种想法的漏洞。

  1. 对于行n遇到的陆地(1),需要记录行n-1才能决定是否要加入到整个陆地面积中去
  2. 对于形如[[1,0,1],[1,1,1]...]的数据在遍历第一行时认为有“两座小岛”,在第二行时需要将其合并)
  3. 不易于判断每座独立岛屿的面积大小

  再来看看DFS的做法是怎样的,Show code:

 1 public class Solution {
 2         public int MaxAreaOfIsland(int[][] grid)
 3         {
 4             int size = 0;
 5 
 6             for (int i = 0; i < grid.Length; i++)
 7             {
 8                 for (int j = 0; j < grid[0].Length; j++)
 9                 {
10                     var temp = dfs(grid, i, j);
11 
12                     size = temp > size ? temp : size;
13                 }
14             }
15 
16             return size;
17         }
18 
19         private int dfs(int[][] grid, int row, int column)
20         {
21             if (row < 0 || column < 0 || row >= grid.Length || column >= grid[0].Length || grid[row][column] == 0)
22             {
23                 return 0;
24             }
25 
26             grid[row][column] = 0;
27 
28             int[] rowFlag = new int[4] { 1, -1, 0, 0 };
29             int[] columnFlag = new int[4] { 0, 0, 1, -1 };
30 
31             int size = 1;
32 
33             for (int i = 0; i < 4; i++)
34             {
35                 size += dfs(grid, row + rowFlag[i], column + columnFlag[i]);
36             }
37 
38             return size;
39         }
40 
41 }

  这种解法也比较直观,在MaxAreaOfIsand中,依次判断了每个结点所在的岛屿的面积大小并取最大值,最后取到的最大值自然就是岛屿的最大面积。

  而在DFS算法中也比较清晰,首先是递归终止条件,超出边界或者是‘海洋’就停止这次的递归。有趣的地方是对于满足条件的‘岛屿’,直接先将其修改为‘海洋’.(grid[row][column]= 0)。这里是为了防止重复的遍历。再来就是对于矩阵类型的题目来说,一组很有意思的数组, rowFlag与columnFlag,将遍历四个方向翻译成遍历数组。 然后开始依次判断该位置四个方向的位置是否满足加入岛屿的条件,并最终返回。

  当然上面这种解法不是最优的,优化解法可以看看官方的利用栈进行优化,当然,这道题目也是可以用BFS来解决的。

 

BFS

1162 地图分析

你现在手里有一份大小为 N x N 的『地图』(网格) grid,上面的每个『区域』(单元格)都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地,你知道距离陆地区域最远的海洋区域是是哪一个吗?请返回该海洋区域到离它最近的陆地区域的距离。

我们这里说的距离是『曼哈顿距离』( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个区域之间的距离是 |x0 - x1| + |y0 - y1| 。

如果我们的地图上只有陆地或者海洋,请返回 -1。

 

示例 1:

 

输入:[[1,0,1],[0,0,0],[1,0,1]]
输出:2
解释:
海洋区域 (1, 1) 和所有陆地区域之间的距离都达到最大,最大距离为 2。
示例 2:

 

输入:[[1,0,0],[0,0,0],[0,0,0]]
输出:4
解释:
海洋区域 (2, 2) 和所有陆地区域之间的距离都达到最大,最大距离为 4。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/as-far-from-land-as-possible
  

  受到前面一道题的启发,这个题目笔者上来就考虑使用DFS进行计算。笔者code如下:

 1         public int MaxDistance(int[][] grid)
 2         {
 3             var result = -1;
 4 
 5             for (int i = 0; i < grid.Length; i++)
 6             {
 7                 for (int j = 0; j < grid.Length; j++)
 8                 {
 9                     if (grid[i][j] == 1)
10                     {
11                         var temp = distanceDfs(grid, 0, 0);
12 
13                         result = result < temp ? temp : result;
14                     }
15                 }
16             }
17 
18             return result;
19         }
20 
21         private int distanceDfs(int[][] grid, int row, int column)
22         {
23             if (row < 0 || column < 0 || row >= grid.Length || column >= grid.Length)
24             {
25                 return 0;
26             }
27 
28             int[] rowf = new int[] { 1, 0 };
29             int[] colf = new int[] { 0, 1 };
30 
31             int disctance = int.MinValue;
32 
33             var guid = Guid.NewGuid().ToString();
34 
35             for (int i = 0; i < 2; i++)
36             {
37                 int temp = 0;
38 
39                 Console.WriteLine($"id:{guid.ToString()} Current index row:{row}, column:{column}");
40 
41                 if (row + rowf[i] < grid.Length && column + colf[i] < grid.Length && grid[row + rowf[i]][column + colf[i]] == 0)
42                 {
43                     Console.WriteLine($"id:{guid.ToString()} Next Current index row:{row + rowf[i]}, column:{column + colf[i]}");
44                     temp = 1 + distanceDfs(grid, row + rowf[i], column + colf[i]);
45                 }
46 
47 
48                 disctance = disctance < temp ? temp : disctance;
49             }
50 
51             Console.WriteLine($"id:{guid.ToString()} Distance:{disctance}");
52             return disctance;
53         }

  当然最终这个方法计算出的结果是不正确的,并且为了调试方便,加入了guid进行每次递归内的数据查看。分析一下没有正确算出答案的原因:这道题其实与岛屿的最大面积看起来类似,但最大面积有着这样一个条件,同一岛屿内点与点之间是相连的。笔者套用这样的方法,边界值又没有处理清楚,就很容易发生混乱,不好理解。

  直接来看看官方的解法吧(C++版本):

 1 class Solution {
 2 public:
 3     static constexpr int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
 4     static constexpr int MAX_N = 100 + 5;
 5 
 6     struct Coordinate {
 7         int x, y, step;
 8     };
 9 
10     int n, m;
11     vector<vector<int>> a;
12 
13     bool vis[MAX_N][MAX_N];
14 
15     int findNearestLand(int x, int y) {
16         memset(vis, 0, sizeof vis);
17         queue <Coordinate> q;
18         q.push({x, y, 0});
19         vis[x][y] = 1;
20         while (!q.empty()) {
21             auto f = q.front(); q.pop();
22             for (int i = 0; i < 4; ++i) {
23                 int nx = f.x + dx[i], ny = f.y + dy[i];
24                 if (!(nx >= 0 && nx <= n - 1 && ny >= 0 && ny <= m - 1)) continue;
25                 if (!vis[nx][ny]) {
26                     q.push({nx, ny, f.step + 1});
27                     vis[nx][ny] = 1;
28                     if (a[nx][ny]) return f.step + 1;
29                 }
30             }
31         }
32         return -1;
33     }
34     
35     int maxDistance(vector<vector<int>>& grid) {
36         this->n = grid.size();
37         this->m = grid.at(0).size();
38         a = grid;
39         int ans = -1;
40         for (int i = 0; i < n; ++i) {
41             for (int j = 0; j < m; ++j) {
42                 if (!a[i][j]) {
43                     ans = max(ans, findNearestLand(i, j));
44                 }
45             }
46         }
47         return ans;
48     }
49 };

  重点在于搜索状态的确定,这里使用‘曼哈顿距离’来作为搜索状态,而递归终止条件则是遇到了“陆地”。因此流程是依次BFS每个非陆地的结点计算。当然这个题目也不止一种解法,另一种的传送门在这里。

 

posted @ 2020-03-29 22:01  DogTwo  阅读(584)  评论(0编辑  收藏  举报