【数据结构与算法】打开转盘锁:使用图的广度优先遍历实现

打开转盘锁:使用图的广度优先遍历实现

Java
https://leetcode-cn.com/problems/open-the-lock/

解题思路

使用图的广度优先遍历思想来实现,字符串处理得比较慢,可以使用哈希方法转换成对应的整型。再者图比较大,搜索速度受限于广度,可以使用双向广度优先遍历优化(没实现,同学们可以自己尝试)。
【数据结构与算法】打开转盘锁:使用图的广度优先遍历实现

在求解问题的时候,之前的都是实实在在给出了可以遍历的情况,从而很明显可以看出那个问题是个图论问题,而在这个问题中,并没有很明显的给出图论问题的模型,需要我们自己去建模。在本问题中,锁的每个状态可以看做是图中的每个顶点,而从一种状态变成另一种状态就是求其“路径”的过程,如上图所示,对于0000这个状态,与其直接连接的(即可以通过一步操作变成的)状态就有8种,随之这8种状态还有很多种,而“1000”与“0100”也都可以通过一步操作到达“1100”状态,所以更加说明其是个多对多的关系,即图所描述的关系,这类问题是关于状态表达的问题。有了以上的转换,那么这个题就转换成了无权无向图的最短路径问题。

代码


import java.util.ArrayList;
import java.util.HashMap;
/**
 * @author Minuy
 * @time 21:04
 * @date 2021/11/20
 */
public class Solution {
    int[] deadends;

    /**
     * 优化,
     * 1. 死亡数组可以转成哈希的形式加快判断
     * 2. 目标不可达可以直接返回
     * 3. 是否遍历跟距离可以一并存储处理
     * 4. 字符串范围为0-9999,整个可以使用整型数字来代替以加快速度
     */
    public int openLock(String[] deadends, String target) {
        // 转换成对应的哈希值,加快判定速度 
        this.deadends = new int[deadends.length];
        for (int i = 0; i < deadends.length; i++) {
            this.deadends[i] = hash(deadends[i]);
        }

        int sta = hash("0000");
        int tar = hash(target);

        if(isDead(tar)){ // 目标不可达
            return -1;
        }

        // 同时承担是否被遍历和存储当前距离的存储任务
        HashMap<Integer, Integer> isVisited = new HashMap<>();

        if (!isDead(sta)) { // 最开始的这个也要判断是不是凉凉的

            ArrayList<Integer> queue = new ArrayList<>();
            queue.add(sta);
            isVisited.put(sta, 0);
            if (tar == sta){ // 目标就是起始位置
                return isVisited.get(tar);
            }

            while (!queue.isEmpty()) {
                sta = queue.remove(0);

                for (int s : getNextStats(sta)) {
                    if (!isVisited.containsKey(s)) {
                        queue.add(s);
                        // isVisited.add(s);
                        isVisited.put(s, isVisited.get(sta) + 1);
                        if (s == tar) {
                            return isVisited.get(tar);
                        }
                    }
                }

            }
        }
        return -1;
    }

    // 把分散的数组值合并
    private int chartsToInt(int[] str) {
       int num = str[3];
       num+=str[2]*10;
       num+=str[1]*100;
       num+=str[0]*1000;
       return num;
    }

    // 把字符串转换成对应的哈希值
    private int hash(String str) {
        int hash = str.charAt(3) - '0';
        hash += (str.charAt(2) - '0') * 10;
        hash += (str.charAt(1) - '0') * 100;
        hash += (str.charAt(0) - '0') * 1000;
        return hash;
    }

    // 获取与sta相邻的状态
    private Iterable<Integer> getNextStats(int sta) {
        ArrayList<Integer> ret = new ArrayList<>();
        int[] chars = new int[4];
        chars[0] = sta / 1000;
        chars[1] = (sta / 100) % 10;
        chars[2] = (sta / 10) % 10;
        chars[3] = sta % 10;
        for (int i = 0; i < chars.length; i++) {
            int o = chars[i]; // 备份
            // ---------------------加一操作
            if (chars[i] == 9) {
                chars[i] = 0;
            } else {
                chars[i] += 1;
            }

            int next = chartsToInt(chars);
            if (!isDead(next)) {
                ret.add(next);
            }

            chars[i] = o; // 恢复一下

            // ---------------------减一操作

            if (chars[i] == 0) {
                chars[i] = 9;
            } else {
                chars[i] -= 1;
            }

            next = chartsToInt(chars);
            if (!isDead(next)) {
                ret.add(next);
            }

            chars[i] = o; // 恢复一下
        }
        return ret;
    }

    // 判断有没有触碰到死亡密码
    private boolean isDead(int str) {
        for (int dead : deadends) {
            if (dead == str) {
                return true;
            }
        }
        return false;
    }

    
}

上一篇:将文字转为汉语拼音


下一篇:NC17 最长回文子串