c/c++ 力扣LeetCode 752.打开转盘锁

30 篇文章 0 订阅

题目链接:

力扣 752.打开转盘锁

不想戳的看下图:
在这里插入图片描述
算法详解:
普通BFS搜索时,每一层的搜索节点数量会爆炸级增加。假设每一次搜索都有 nn 个新的状态,并假设从起点到目标路径长为 mm,那就要搜索:n+n2+n3+…+n^mn+n2 +n3 +…+nm 个状态,状态数就是 n^{m+1}n m+1 数量级的。
所以,此时,双向的BFS就派上用场了。

原理:
双向BFS是同时从起点和终点两个方向开始搜索,一旦搜索到另一个方向已经搜索过的位置(或者说出现某个状态被两个方向均访问到了),就意味着找到了一条联通起点和终点的最短路径。
双向BFS同时从起点和终点两个方向进行搜索,呈「两面包夹芝士」向最短路中间的某一点汇集,在路径中点相遇。则双向BFS的状态数是 2*n^{m/2+1}2∗n m/2+1 数量级的。就这样优化了复杂度

解题思路:
朴素BFS就是单向BFS,单向BFS需要一个队列来进行搜索,需要一个哈希表来解决重复搜索记录搜索层数。
那双向BFS就需要**「两个队列」来进行双向搜索!需要「两个哈希表」来分别解决各自方向的重复搜索记录搜索**层数(也是字符串转换次数)!

代码如下:

class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        unordered_set<string> dead(deadends.begin(), deadends.end());  
        if (dead.count("0000")) {
            return -1;
        }
        if (target == "0000") {
            return 0;
        }

        queue<string> q1, q2;    
        q1.emplace("0000");
        q2.emplace(target);

        unordered_map<string, int> mp1, mp2;
        mp1["0000"] = 0;
        mp2[target] = 0;

        int res = -1;

        //如果其中一个队列空了,搜索结束
        while (!q1.empty() && !q2.empty()) {
            //选择一个容量更少的队列进行BFS搜索
            if (q1.size() <= q2.size()) {
                res = bfs(q1, mp1, mp2, dead);
            } else {
                res = bfs(q2, mp2, mp1, dead);
            }
            if (res != -1) return res;
        }
        return -1;  
    }


    //字符正向转函数
    char next_c(char c) {
        return c == '9' ? '0' : c + 1;
    }

    //字符反向转函数
    char prev_c(char c) {
        return c == '0' ? '9' : c - 1;
    }

    //q为本方向的搜索队列,mine为本方向的哈希表,other为另外一方向的哈希表
    int bfs(queue<string>& q, unordered_map<string, int>& mine, unordered_map<string, int>& other, unordered_set<string>& dead) {
        string cur = q.front();
        int step = mine[cur];
        q.pop();

        for (int i = 0; i < 4; ++i) {
            //j为-1时,字符反向转(即减1)
            //j为1时,字符正向转(即加1)
            for (int j = -1; j <= 1; j += 2) {
                //转换字符
                cur[i] = (j == -1) ? prev_c(cur[i]) : next_c(cur[i]);
                //如果字符转换后的字符串不是dead字符串,也没有搜索过,则对该字符串进行更新。
                if (!mine.count(cur) && !dead.count(cur)) {
                    //如果该字符串在另一个方向已经找到过,说明两个方向在本字符串处汇集,找到了最短路;否则加入队列
                    if (other.count(cur)) {
                        return step + 1 + other[cur];
                    } else {
                        mine[cur] = step + 1;
                        q.emplace(cur);
                    }
                }
                //恢复前面字符的转换,保证本次bfs中下一次循环时原本字符串cur不变
                cur[i] = (j == -1) ? next_c(cur[i]) : prev_c(cur[i]);
            }
        }
        return -1;
    }
};

小结

这一题运用到了双向的BFS,有一定操作难度,需要多读几遍,适当性的理解,才能学好这个算法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值