• BFS算法框架


    BFS算法框架

      BFS的核心思想,就是把一些问题抽象成图,从一个节点开始,向四周扩散。一般来说,写BFS都是用[队列]这个数据结构,每次将一个节点周围的节点加入到队尾。

      BFS相对于DFS的最主要区别是:BFS找到的路径一定是最短的,但代价就是空间复杂度比DFS大很多。本文从两道经典的BFS题目来讲解。

    先举例⼀下 BFS 出现的常⻅场景好吧, 问题的本质就是让你在⼀幅「图」 中找到从起点 start 到终点 target 的最近距离, 这个例⼦听起来很枯燥, 但是 BFS 算法问题其实都是在⼲这个事⼉, 把枯燥的本质搞清楚了, 再去欣赏各种问题的包装才能胸有成竹嘛。

    • ⽐如⾛迷宫, 有的格⼦是围墙不能⾛, 从起点到终点的最短距离是多少?如果这个迷宫带「传送门」 可以瞬间传送呢?

    • ⽐如说两个单词, 要求你通过某些替换, 把其中⼀个变成另⼀个, 每次只能替换⼀个字符, 最少要替换⼏次?

    • ⽐如说连连看游戏, 两个⽅块消除的条件不仅仅是图案相同, 还得保证两个⽅块之间的最短连线不能多于两个拐点。 你玩连连看, 点击两个坐标, 游戏是如何判断它俩的最短连线有⼏个拐点的?

      这些问题都没啥奇技淫巧, 本质上就是⼀幅「图」 , 让你从⼀个起点, ⾛到终点, 问最短路径。 这就是 BFS 的本质, 框架搞清楚了直接默写就好。

    BFS框架:

    int BFS(Node start, Node target){
        // 核心数据结果:队列
        queue<Node> q;
        // 标记集合,避免走回头路
        set<Node> visited;
        // 首先将起点加入队尾
        q.push(start);
        // 记录扩散的次数(其实就是要求的路径长度)
        int step = 0;
        while(!q.empty()){
            int sz = q.size();
            // 将当前队列中的所有节点向其”周围(图就是邻接点,二叉树就是子节点)“扩散
            for(int i = 0; i < sz; i++){
                // 获取队首元素并将其出列
                Node cur = q.front();
                q.pop();
                // 划重点,这里判断是否到达终点
                if(cur is target)
                    return step;
                
                // 将当前节点cur的所有相邻节点加入队尾
                for(Node p : cur.adj()){
                    if(p not in visited){
                        // 加入队尾
                        q.push(p);
                        // 标记
                        visited.add(p);
                    }
                }
            }
            // 划重点,更新步数在这里
            step++;
        }
    }
    
    其中,cur.adj()表示节点cur的相邻节点(图即邻接点,树即子节点)。

    经典例题一:二叉树的最小高度(leetcode.111)

      怎么套到 BFS 的框架⾥呢?⾸先明确⼀下起点 start 和终点 target 是什么,怎么判断到达了终点?显然起点就是 root 根节点, 终点就是最靠近根节点的那个「叶⼦节点」嘛, 叶⼦节点就是两个⼦节点都是 null 的节点:

    if(cur->left == nullprt && cur->right == nullptr)
    	//到达叶子结点,返回
    
      那么,按照BFS框架稍加改造:
    #include <queue>
    
    /**
     * 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:
        int minDepth(TreeNode* root) {
            if(root == nullptr)
                return 0;
            queue<TreeNode*> q;
            // 将起点加入到队尾
            q.push(root);
            int depth = 1;
            while(!q.empty()){
                int sz = q.size();
                // 将当前队列中的所有节点向四周扩散
                for(int i = 0; i < sz; i++){
                    // 每次取出队首节点
                    TreeNode* cur = q.front();
                    q.pop();
                    // 这里判断是否到达终点
                    if(cur->left == nullptr && cur->right == nullptr)
                        return depth;
                    // 将cur的相邻节点加入队尾
                    if(cur->left != nullptr)
                        q.push(cur->left);
                    if(cur->right != nullptr)
                        q.push(cur->right);
                }
                // 划重点,更新步数在这里
                depth++;
            }
            return depth;
        }
    };
    

    经典例题二:打开转盘锁(leetcode.752)

    直接上代码:

    #include <string>
    #include <queue>
    
    class Solution {
    public:
        // 将s[i]向上拨
        string plusOne(string s, int i){
            if(s[i] =='9')
                s[i] = '0';
            else 
                s[i] = s[i] + 1;
            return s;
        }
    
        // 将s[i]向下拨
        string minusOne(string s, int i){
            if(s[i] == '0')
                s[i] = '9';
            else
                s[i] = s[i] - 1;
            return s;
        }  
    
        int BFS(vector<string> &deadends, string target){
            queue<string> q;
            set<string> visited;
            set<string> deads;
            for(int i = 0; i < deadends.size(); i++)
                deads.insert(deadends[i]);
            int step = 0;
            q.push("0000");
            visited.insert("0000");
            while(!q.empty()){
                int sz = q.size();
                for(int i = 0; i < sz; i++){
                    string cur = q.front();
                    q.pop();
                    // 跳过死锁
                    if(deads.find(cur) != deads.end())
                        continue;
                    if(cur == target){
                        printf("%d
    ", step);
                        return step;
                    }
                    // 将该结点的所有子节点加入队尾
                    for(int i = 0; i < 4; i++){
                        string adjPlus = plusOne(cur, i);
                        string adjMinus = minusOne(cur, i);
                        if(visited.find(adjPlus) == visited.end()){
                            q.push(adjPlus);
                            visited.insert(adjPlus);
                        }
                        if(visited.find(adjMinus) == visited.end()){
                            q.push(adjMinus);
                            visited.insert(adjMinus);
                        }
                    }
                }
                step++;
            }
            return -1;
        }
    
        int openLock(vector<string>& deadends, string target) {
            return BFS(deadends, target);
        }
    };
    
  • 相关阅读:
    https://pingcap.com/blog-cn/flame-graph/
    https://software.intel.com/sites/landingpage/pintool/docs/97998/Pin/html/
    http://boostorg.github.io/stacktrace/stacktrace/getting_started.html#stacktrace.getting_started.how_to_print_current_call_stack
    线程局部存储
    slice 切片实现 Slice object interface
    网络分裂 redis 集群
    vscode-sftp
    Sizes of integer types 整形字节长度 系统字节
    学件中心
    源码 版本
  • 原文地址:https://www.cnblogs.com/flyingrun/p/13567847.html
Copyright © 2020-2023  润新知