刷题的一天——BFS和DFS

1、给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。两个相邻元素间的距离为 1 。

示例:输入:mat = [[0,0,0],[0,1,0],[0,0,0]] 输出:[[0,0,0],[0,1,0],[0,0,0]]

对于这个题我们首先可以想到广度优先搜索。

(1)BFS

代码:

class Solution {
public:
    static constexpr int dirs[4][2]{{-1,0},{1,0},{0,-1},{0,1}};       //设置BFS的移动方向(这里的设置方式可以学习)
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
    int m = mat.size(),n = mat[0].size();                              
    vector<vector<int>> dist (m,vector<int>(n));                     //diste矩阵,记录距离
    vector<vector<int>> seen (m,vector<int>(n));                     //记录0所在位置,同时在计算了非0数与0的距离后排除非0数
    queue<pair<int,int>>q;                                           //队列中加入0元素
     for(int i = 0;i<m;++i){
         for(int j = 0;j<n;++j){
             if(mat[i][j]==0){
                 q.emplace(i,j);                                      //把0全部放入q矩阵,记录坐标
                 seen[i][j]=1;                                           //0不参与距离的计算
             }
         }
     }
     while(! q.empty()){
         auto[i,j]=q.front();                                           //以q中的0坐标为基准
         q.pop();
         for(int d = 0;d<4;++d){                                          //进行广度有限搜索
             int ni = i+dirs[d][0];
             int nj = j+dirs[d][1];                                      
             if(ni>=0 && ni<m && nj>= 0 && nj<n && !seen[ni][nj]){         //非0且没有计算过的坐标进行计算并标记
                 dist[ni][nj] = dist[i][j] +1;
                 q.emplace(ni,nj);
                 seen[ni][nj]=1;

             }
         }
     }



    return dist;
    }
};

时间复杂度为O(mn),空间复杂度为O(mn)。

(2)动态规划

还可以使用动态规划,也就是把问题进行拆解。比如一直保持一个水平方向和一个垂直方向的移动,最后得到最小距离。

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        // 初始化动态规划的数组,所有的距离值都设置为一个很大的数
        vector<vector<int>> dist(m, vector<int>(n, INT_MAX / 2));
        // 如果 (i, j) 的元素为 0,那么距离为 0
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0) {
                    dist[i][j] = 0;
                }
            }
        }
        // 只有 水平向左移动 和 竖直向上移动,注意动态规划的计算顺序
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (i - 1 >= 0) {
                    dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1);
                }
                if (j - 1 >= 0) {
                    dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1);
                }
            }
        }
        // 只有 水平向左移动 和 竖直向下移动,注意动态规划的计算顺序
        for (int i = m - 1; i >= 0; --i) {
            for (int j = 0; j < n; ++j) {
                if (i + 1 < m) {
                    dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1);
                }
                if (j - 1 >= 0) {
                    dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1);
                }
            }
        }
        // 只有 水平向右移动 和 竖直向上移动,注意动态规划的计算顺序
        for (int i = 0; i < m; ++i) {
            for (int j = n - 1; j >= 0; --j) {
                if (i - 1 >= 0) {
                    dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1);
                }
                if (j + 1 < n) {
                    dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1);
                }
            }
        }
        // 只有 水平向右移动 和 竖直向下移动,注意动态规划的计算顺序
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                if (i + 1 < m) {                                            //一定要注意这个边界,防止溢出
                    dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1);
                }
                if (j + 1 < n) {
                    dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1);
                }
            }
        }
        return dist;
    }
};

最后得到时间复杂度为O(mn),但空间复杂度减少为O(1)。这里除了答案数组dist以外,没有用到额外的数组。因此动态规划在性能上是优于BFS的。但也可以把代码进一步优化,比如只留下左上和右下方向的动态规划。时间空间复杂度差不多但是代码上更加简洁。

2、在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

示例:

输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
输出:4

很容易可以想到广度优先搜索,搜到了新鲜橘子就让他变腐烂。

class Solution {
public:
   int orangesRotting(vector<vector<int>>& grid) {
        int row=grid.size();
        int col=grid[0].size();
        int res=0;
        vector<int>dx={-1,0,0,1};//辅助定位即将被腐烂的橘子的横坐标
        vector<int>dy={0,1,-1,0};//辅助定位即将被腐烂的橘子的纵坐标,对应构成腐烂橘子的四个污染方向
        queue<pair<int,int>>rot ;
        for(int i=0;i<row;++i)//将腐烂橘子一一压入队列
            for(int j=0;j<col;++j)
                if(grid[i][j]==2)
                    rot.push({i,j});
        while(!rot.empty())
        {
            int vol=rot.size();//标记队列内腐烂橘子个数
            for(int i=0;i<vol;++i)
            {
                pair<int,int> fir=rot.front();//取出首个腐烂橘子
                rot.pop();
                for(int j=0;j<4;++j)//进行四个方向污染
                {
                    int x=fir.first+dx[j],y=fir.second+dy[j];
                    if(x>=0&&x<row&&y>=0&&y<col&&grid[x][y]==1)//判断是否存在新鲜橘子
                    {
                        grid[x][y]=2;
                        rot.push({x,y});
                    }
                }
            }
            if(!rot.empty())//如果为空表示一开始就没有腐烂橘子,返回0分钟
                res++;//每次取出队列所有橘子时间加1,同时压入被污染的新一批橘子
        }
        for(int i=0;i<row;++i)//检查是否还有新鲜橘子
            for(int j=0;j<col;++j)
                if(grid[i][j]==1)
                    return -1;
        return res;
    }
};

最后得到时间复杂度O(mn),空间复杂度O(1),在数组本身进行修改,就设置了一个队列而无其他消耗空间的组件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值