BFS“填海造陆”

先来看下题目:
在这里插入图片描述
看到题目后,先想到了BFS的做法,但是却找错了“中心点”;我一开始是将海洋作为遍历的中心点,然后找每个海洋与所有陆地的距离后求出最小距离,然后等到所有海洋都被遍历完后,取之前求过的最小距离中的最大值即为答案,代码是这样的:

class Solution {
public:
    int maxDistance(vector<vector<int>>& grid) {
        if(grid.size()==0)return 0;
         queue<pair<int,int>>sea;
         vector<pair<int,int>>land;
           for(int i=0;i<grid.size();i++){
               for(int j=0;j<grid[0].size();j++){
                   if(grid[i][j]==1)land.push_back({i,j});
                   else if(grid[i][j]==0)sea.push({i,j});
               }
           }
           if(sea.size()==0||land.size()==0)return -1;
           vector<int>mindistance;
           int distance=0;
           while(!sea.empty()){
               int temp=INT_MAX;
               for(auto l:land){
                   int x=abs(sea.front().first-l.first);
                   int y=abs(sea.front().second-l.second);
                   temp=min(temp,x+y);
               }
               sea.pop();
               distance=max(distance,temp);
           }
           return distance;
    }
};

测试用例通过,然后提交,果然超时。在这里插入图片描述
直到我看了最后的测试用例(如下图)明白了一切:当地图非常大时,这种以海洋为中心的BFS会显得非常复杂且低效。那么,能不能换个思路呢?
在这里插入图片描述
看了评论区的解答后,发现另一种做法是以“陆地”为中心的“链式扩展”BFS,也就是“填海造陆”的做法,这种做法效率显然要高得多。
那么为什么可以这么做呢?稍加思考之后我们可以发现这题跟LeetCode上“腐烂的橘子”本质上是换汤不换药的两个题目,这道题我们先把陆地(坏橘子)加入队列里,然后判断是否全部是陆地(坏橘子)或海洋(新鲜橘子);最后进行BFS搜索,遇到与陆地相邻的海洋时,将其数字修改并加入陆地队列(腐烂新鲜橘子),直到最后没有海洋存在,返回遍历的层数即为所求最大距离。这样的过程我们可以这样来直观的理解:开始时我可以从任意一块陆地出发(最开始加入队列的陆地),经过一步我可以到达哪些海洋(初始队列紧邻的海洋),然后我再以这些点出发进过一步又可以到达哪些海洋,因为我们每一步都是选择紧邻自己的海洋,所以到达每个海洋我都是挑所有可路线里最近的那条来走,所以说这样来走的话,最后才走到的那个海洋一定是离所有陆地的最近距离是最远的(否则我不会最后一个到达它)。

我们都知道BFS的代码框架式这样的:(几乎所有求最短路径的问题,都可以用BFS解决)

while queue 非空:
	node = queue.pop()
    for node 的所有相邻结点 m:
        if m 未访问过:
            queue.push(m)

而为了做到层序遍历,我们必须保证当前层遍历完成后才继续下一层,如果用上面这种写法来遍历的话,我们是无法区分 BFS 遍历中的每一“层”的。这是因为,遍历的时候,第 1 层的结点还没出完队列,第 2 层的结点就进来了。这个队列中第 1 层和第 2 层的结点会紧挨在一起,无法区分,也就无法知道每个结点的距离 depth 了。

depth = 0 # 记录遍历到第几层
while queue 非空:
    depth++
    n = queue 中的元素个数
    循环 n 次:
        node = queue.pop()
        for node 的所有相邻结点 m:
            if m 未访问过:
                queue.push(m)

而在此题中,因为初始队列是陆地(还没开始走),所以depth=-1;例外要注意地图边界情况。

public int maxDistance(int[][] grid) {
    int N = grid.length;
    
    Queue<int[]> queue = new ArrayDeque<>();
    // 将所有的陆地格子加入队列
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (grid[i][j] == 1) {
                queue.add(new int[]{i, j});
            }
        }
    }
    // 如果我们的地图上只有陆地或者海洋,请返回 -1。
    if (queue.isEmpty() || queue.size() == N * N) {
        return -1;
    }
    
    int distance = -1;
    while (!queue.isEmpty()) {
        distance++;
        int n = queue.size();
        // 这里一口气取出 n 个结点,以实现层序遍历
        for (int i = 0; i < n; i++) {
            int[] cell = queue.poll();
            int r = cell[0];
            int c = cell[1];
            // 遍历上方单元格
            if (r-1 >= 0 && grid[r-1][c] == 0) {
                grid[r-1][c] = 2;
                queue.add(new int[]{r-1, c});
            }
            // 遍历下方单元格
            if (r+1 < N && grid[r+1][c] == 0) {
                grid[r+1][c] = 2;
                queue.add(new int[]{r+1, c});
            }
            // 遍历左边单元格
            if (c-1 >= 0 && grid[r][c-1] == 0) {
                grid[r][c-1] = 2;
                queue.add(new int[]{r, c-1});
            }
            // 遍历右边单元格
            if (c+1 < N && grid[r][c+1] == 0) {
                grid[r][c+1] = 2;
                queue.add(new int[]{r, c+1});
            }
        }
    }
    
    return distance;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值