LeetCode752:打开转盘锁(Java)

如题image-20220412230720405

  • 分析题目

    • 题目用长度为4的字符串表示密码,我们转动密码的时候每一位上只能上或下转动一次,我们可以理解转动一位密码就相当于更改这个密码字符串其中一个字符的值,因此我们可以编写两个方法,分别代表将密码字符串中的某一位向上移动一位和向下移动一位

      //将密码字符串s的第t位向上拨动一位
      public String moveUp(String s,int t){
          char[] chars = s.toCharArray();//将当前密码字符串转成char数组,便于更改每一位的值,最后将这个char数组转成字符串
          if (chars[t]=='9'){//当值为9时向上拨动应该为0
              chars[t]='0';
          }else {
              chars[t] += 1;//其它情况下就是当前字符值+1
              //注意这里是加int类型的1而不是char类型的1,因为char类型底层是对应ASCII的,ASCII码是用int类型的值对应查找char类型的字符,
              //而'1','2'这些字符在码表中是连续的,我们将'1'的ASCII码值+1可以得到'2',
              //但是如果加的是字符类型的'1'那么它的ASCII码值并不是1,此时得出的字符并不是'2'
          }
          return new String(chars);
      }
      //同理
      public String moveDown(String s,int t){
          char[] chars = s.toCharArray();
          if (chars[t]=='0'){
              chars[t]='9';
          }else {
              chars[t] -= 1;
          }
          return new String(chars);
      }
      
  • 思路

    • 我们要从当前'0000'开始找到正确的密码,那么就需要穷举所有的可能来判断是否和目标target值相等

    • 如何穷举

      • 当我们在转动密码的时候我们可以转动四位中的任何一位,而每次转动有两个方向,因此我们每次转动后有8中可能出现的结果。我们可以理解成从当前状态做出一个操作后变到下一个状态,将这两个状态用线连接起来表示他们是相邻的两个状态,因此第一个状态后会有8根线连着代表接下来有8个可能状态。然后我们再从这8个状态中依次进行相同的操作,此时我们可以发现:这很像无向图的遍历
        • 因此我们可以将穷举所有可能抽象理解为遍历一个无向图!!!
    • 图的遍历有两种方式

      1. DFS遍历
      2. BFS层次遍历
      • 这里我们应该使用BFS遍历的方法,因为我们需要计算从'0000'至少转动多少下能够找到正确密码,而**BFS层次遍历的时候每一层就相当于转动了一次,最终找到正确密码的时候我们只需计算它当前在遍历的第几层,这就是找到它的最短路径**!
    • 题目中额外有一个deadends数组,我们转动密码的时候要注意不能转动成这个状态,否则密码会被锁定,因此我们可以用一个HashSet来保存这个数组的值,当我们遍历到一个新状态的时候就判断这个状态是否在这个HashSet集合中,如果是就终止这个状态再往下遍历;如果不是就在这个状态下继续遍历

  • 代码

    class Solution {
        public int openLock(String[] deadends, String target) {
            Queue<String> q = new LinkedList<>();//BFS层次遍历需要用到的队列
            HashSet<String> deadendSet = new HashSet<>(Arrays.asList(deadends));//保存会导致锁定的密码值
            HashSet<String> visited = new HashSet<>();//保存哪些状态我们遍历过,即哪些密码值我们有转到过,如果遍历过就不加入队列中
            String start = "0000";
            q.offer(start);//往队列中加入初始值
            visited.add(start);//将初始值设置为遍历过的状态
            int stepCount=0;//用来记录找到正确密码的最少步数
            
            while (!q.isEmpty()){//队列非空时一直遍历
                int size = q.size();//记录当前层有多少个结点,遍历完这些结点后遍历下一层
                for (int j = 0; j < size; j++) {
                    String s = q.poll();//队头元素出队
                    if (deadendSet.contains(s))continue;//如果当前状态会导致锁定,就跳过当前此次循环,即停止从该层向下遍历
                    if (target.equals(s))return stepCount;//如果找到了目标,就返回当前记录的步数
                    for (int i = 0; i < 4; i++) {//这个密码四个位置每个位置都可以转动,一次遍历
                        String up = moveUp(s, i);//先向上转动
                        if (!visited.contains(up)){//如果向上转动后的值我们未曾遍历过,就加入队列,否则不加入
                            q.offer(up);
                            visited.add(up);
                        }
                        String down = moveDown(s,i);//再向下转动
                        if (!visited.contains(down) ){//如果向下转动后的值我们未曾遍历过,就加入队列,否则不加入
                            q.offer(down);
                            visited.add(down);
                        }
                    }
                }
                stepCount++;//遍历完当前层,层次+1,代表当前层次
            }
            return -1;
        }
        
        public String moveUp(String s,int t){
            char[] chars = s.toCharArray();
            if (chars[t]=='9'){
                chars[t]='0';
            }else {
                chars[t] += 1;
            }
            return new String(chars);
        }
        public String moveDown(String s,int t){
            char[] chars = s.toCharArray();
            if (chars[t]=='0'){
                chars[t]='9';
            }else {
                chars[t] -= 1;
            }
            return new String(chars);
        }
    }
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一酒。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值