leetcode752:打开转盘锁

这篇博客主要介绍了如何运用广度优先搜索(BFS)算法解决一个数字转盘锁的最短路径问题。在给定一系列死亡密码和目标密码的情况下,通过移动每个数字加一或减一的操作,寻找从初始密码“0000”到达目标密码的最短步数。博主详细解释了BFS算法的应用,并给出了两种不同的实现方式,其中一种利用了两个集合交替进行扩散,确保不重复访问已探索过的路径。文章还对比了二叉树的最小深度问题,强调了BFS在寻找最短路径问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
思路:如果没有限制条件target和死亡锁deadends,那么这道题就转换为了排列组合,问题的关键是每次只能移动一个数字,并且是移动的数字与原有位置上的数字相差一个单位。那么针对一个数字串,有几种可能呢?一个数字可以向上向下下两种,那么一个字符串是4个字符组成,就转变成了一个字符串可以有八种可能。而针对这八种可能,以每个字符串作为基准元素,往下又有8种可能。所以这看上去就是BFS算法,而题目给了目标字符串target,我们的目的是要找到target,并且返回找到的这个target的最短路径,凡是求最短路径,那就是BFS的套路了。
我们回想一下BFS算法中,如何计算二叉树中最低的深度?
思路是:先把根节点放入到队列中,不断的从队列中拿出元素,递归终止条件就是当根节点的左右子树都为空时,那么就得到了最低深度的二叉树的值为多少?为了方便大家理解,我们将代码给出。

 public int minDepth(TreeNode root) {
        if(root==null) return 0;
        Queue<TreeNode> q = new LinkedList<>();
        int depth = 1;
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            for(int i = 0; i< size;i++){
                TreeNode curNode = q.poll();
                // 判断是否到达终点
                if(curNode.left == null && curNode.right == null){
                    return depth; 
                }if(curNode.left!=null){
                    q.offer(curNode.left);
                }if(curNode.right!=null){
                    q.offer(curNode.right);
                }
            }
            depth++;
        }
        return depth;

    }

那么回到刚才的求转盘锁的问题,又何尝不是求BFS中路径最短的距离呢,说白了就是多叉树的深度问题。而由于转盘的数字转动过程中,可能会有已经访问过的,要求不能重复,所以我们定义一个set visited集合记录已经访问过得字符串,而对于deadends,我们仍然将其存放到另一个Set deads集合中,每次都要判断是否visited访问过了,若访问过了,则直接continue。

class Solution {
    //首先当向上波动转盘时,会发生什么?
    String upOne(String s,int j){
        char[] ch = s.toCharArray();
        if(ch[j] == '9') ch[j] = '0';
        else{ ch[j] +=1;}
        return new String(ch);
    }
    //首先当向下转动转盘时,会发生什么?
    String downOne(String s,int j){
        char[] ch = s.toCharArray();
        if(ch[j] == '0') ch[j] = '9';
        else{ ch[j] -=1;}
        return new String(ch);
    }
    public int openLock(String[] deadends, String target) {
        // 记录需要跳过的死亡密码
        Set<String> deads = new HashSet<>();
        for(String s:deadends) deads.add(s);
        // 记录访问过的转盘字符串
        Set<String> visited = new HashSet<>();
        // 放置一个队列,让每个BFS的字符串进入到队列中
        Queue<String> q = new LinkedList<>();
        int step = 0;
        q.add("0000");
        visited.add("0000");
        while(!q.isEmpty()){
            int sz = q.size();
            
            for(int i = 0;i<sz;i++){
                String cur  = q.poll();
                if(deads.contains(cur)){
                    continue;
                }
                if(cur.equals(target)){
                    return step;
                }
                 /* 将一个节点的未遍历相邻节点加入队列 */
                for(int j=0;j<4;j++){
                    String up = upOne(cur,j);
                    if(!visited.contains(up)){
                        q.add(up);
                        visited.add(up);
                    }
                    String down = downOne(cur,j);
                    if(!visited.contains(down)){
                        q.add(down);
                        visited.add(down);
                    }
                }
            }
            step++;
            

        }
        return -1;

    }
}
class Solution {
    //首先当向上波动转盘时,会发生什么?
    String plusOne(String s,int j){
        char[] ch = s.toCharArray();
        if(ch[j] == '9') ch[j] = '0';
        else{ ch[j] +=1;}
        return new String(ch);
    }
    //首先当向下转动转盘时,会发生什么?
    String minusOne(String s,int j){
        char[] ch = s.toCharArray();
        if(ch[j] == '0') ch[j] = '9';
        else{ ch[j] -=1;}
        return new String(ch);
    }
    int openLock(String[] deadends, String target) {
        Set<String> deads = new HashSet<>();
        for (String s : deadends) deads.add(s);
        // 用集合不用队列,可以快速判断元素是否存在
        Set<String> q1 = new HashSet<>();
        Set<String> q2 = new HashSet<>();
        Set<String> visited = new HashSet<>();
        
        int step = 0;
        q1.add("0000");
        q2.add(target);
    
    while (!q1.isEmpty() && !q2.isEmpty()) {
        // 哈希集合在遍历的过程中不能修改,用 temp 存储扩散结果
        Set<String> temp = new HashSet<>();

        /* 将 q1 中的所有节点向周围扩散 */
        for (String cur : q1) {
            /* 判断是否到达终点 */
            if (deads.contains(cur))
                continue;
            if (q2.contains(cur))
                return step;
            
            visited.add(cur);

            /* 将一个节点的未遍历相邻节点加入集合 */
            for (int j = 0; j < 4; j++) {
                String up = plusOne(cur, j);
                if (!visited.contains(up))
                    temp.add(up);
                String down = minusOne(cur, j);
                if (!visited.contains(down))
                    temp.add(down);
            }
        }
        /* 在这里增加步数 */
        step++;
        // temp 相当于 q1
        // 这里交换 q1 q2,下一轮 while 就是扩散 q2
        q1 = q2;
        q2 = temp;
    }
    return -1;
}
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值