• BFS - 常见算法问题


    BFS

    广度优先搜索
    应用一:层序遍历

    应用二最短路径

    题目

    机器人的运动范围

    典型题例:

    地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼n−1。
    一个机器人从坐标 (0,0) 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。
    但是不能进入行坐标和列坐标的数位之和大于 k 的格子。
    请问该机器人能够达到多少个格子?

    示例 :

    输入:k=18, m=40, n=40
    
    输出:1484
    
    解释:当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
          但是,它不能进入方格(35,38),因为3+5+3+8 = 19。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    思路
    ( B F S ) O ( n m ) (BFS) O(nm) (BFS)O(nm)
    这是一个典型的宽度优先搜索问题,我们从 ( 0 , 0 ) (0, 0) (0,0) 点开始,每次朝上下左右四个方向扩展新的节点即可。
    扩展时需要注意新的节点需要满足如下条件:

    • 之前没有遍历过,这个可以用个bool数组来判断;
    • 没有走出边界;
    • 横纵坐标的各位数字之和小于 k k k

    最后答案就是所有遍历过的合法的节点个数。

    时间复杂度
    每个节点最多只会入队一次,所以时间复杂度不会超过方格中的节点个数。
    最坏情况下会遍历方格中的所有点,所以时间复杂度就是 O ( n m ) O(nm) O(nm)

    代码:

    class Solution {
    public:
        
        int get_sum(pair<int,int> p){
            
            int s = 0;
            while(p.first){
                
                s += p.first % 10;
                p.first /= 10;
            }
            
            while(p.second){
                
                s += p.second % 10;
                p.second /= 10;
            }
            
            return s;
        }
        
        int movingCount(int threshold, int rows, int cols)
        {
            if(!rows || !cols)  return 0;
            
            queue<pair<int, int>> q;
            vector<vector<bool>> st(rows, vector<bool>(cols, false));
            
            int dx[4] = {0, 1, 0, -1}, dy[4] = {-1, 0, 1, 0};
            
            int res = 0;
            q.push({0, 0});
            while(q.size()){
                
                auto t = q.front();
                q.pop();
                
                if(st[t.first][t.second] || get_sum(t) > threshold)    continue;
                res ++;
                st[t.first][t.second] = true;
                
                for(int i = 0; i < 4; i ++){
                    
                    int x = t.first + dx[i], y = t.second + dy[i];
                    //printf("x = %d, y = %d, res = %d\n", x, y, res);
                    if(x >= 0 && x < rows && y >= 0 && y < cols)    q.push({x, y});
                }
            }
            
            return res;
        }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    之字形打印二叉树

    典型题例:

    请实现一个函数按照之字形顺序从上向下打印二叉树。
    即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

    示例 :

    输入如下图所示二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]
        8
       / \
      12  2
         / \
        6   4
    输出:[[8], [2, 12], [6, 4]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    思路
    核心
    ( B F S ) O ( n ) (BFS) O(n) (BFS)O(n)
    宽度优先遍历,一层一层来做。即:

    1. 将根节点插入队列中;
    2. 创建一个新队列,用来按顺序保存下一层的所有子节点;
    3. 对于当前队列中的所有节点,按顺序依次将儿子插入新队列;
    4. 按从左到右、从右到左的顺序交替保存队列中节点的值;
    5. 重复步骤 2 − 4 2-4 24,直到队列为空为止。

    时间复杂度
    树中每个节点仅会进队出队一次,所以时间复杂度是 O ( n ) O(n) O(n)

    代码:

    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     * };
     */
    class Solution {
    public:
        vector<vector<int>> printFromTopToBottom(TreeNode* root) {
    
            vector<vector<int>> res;
            if (!root)  return res;
    
            queue<TreeNode*> q;
            q.push(root);
            q.push(nullptr);
    
            vector<int> level;
            bool zigzag = false;    //为false时不反转数组
            while (q.size()){
    
                auto t = q.front();
                q.pop();
                if (!t){            
    
                    if(level.empty())   break;
                    if(zigzag)  reverse(level.begin(), level.end());    //反转数组
                    res.push_back(level);
                    level.clear();
                    q.push(nullptr);
                    zigzag = !zigzag;   //标记反转
                    continue;
                }
                level.push_back(t->val);
                if (t->left)    q.push(t->left);
                if (t->right)   q.push(t->right);
            }
    
            return res;
        }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    充电站
    推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

  • 相关阅读:
    my2sql工具之快速入门
    Nginx HTTP框架综述
    阿里云认证 | 2023年ACP认证考试大揭秘
    【每日一题】Day 38 选择题
    Druid实现SQL监控、慢SQL记录、Spring监控、去广告
    【杂记-浅谈OSPF协议中的Router ID】
    半小时速通Python爬虫!GitHub开源的Python爬虫入门教程
    前端TypeScript学习-交叉类型与泛型
    Hive之数据类型和视图
    IP网络广播景区广播广播系统
  • 原文地址:https://blog.csdn.net/weixin_53492721/article/details/127944407