剑指 Offer II 109. 开密码锁(752. 打开转盘锁)

题目:

 

 

 

思路:

【1】广度优先搜索:

代码展示:

//时间63 ms击败79.94%
//内存45 MB击败74.14%
class Solution {
    public int openLock(String[] deadends, String target) {
        if ("0000".equals(target)) {
            return 0;
        }

        Set<String> dead = new HashSet<String>();
        for (String deadend : deadends) {
            dead.add(deadend);
        }
        if (dead.contains("0000")) {
            return -1;
        }

        int step = 0;
        Queue<String> queue = new LinkedList<String>();
        queue.offer("0000");
        Set<String> seen = new HashSet<String>();
        seen.add("0000");

        while (!queue.isEmpty()) {
            ++step;
            int size = queue.size();
            for (int i = 0; i < size; ++i) {
                String status = queue.poll();
                for (String nextStatus : get(status)) {
                    if (!seen.contains(nextStatus) && !dead.contains(nextStatus)) {
                        if (nextStatus.equals(target)) {
                            return step;
                        }
                        queue.offer(nextStatus);
                        seen.add(nextStatus);
                    }
                }
            }
        }

        return -1;
    }

    public char numPrev(char x) {
        return x == '0' ? '9' : (char) (x - 1);
    }

    public char numSucc(char x) {
        return x == '9' ? '0' : (char) (x + 1);
    }

    // 枚举 status 通过一次旋转得到的数字
    public List<String> get(String status) {
        List<String> ret = new ArrayList<String>();
        char[] array = status.toCharArray();
        for (int i = 0; i < 4; ++i) {
            char num = array[i];
            array[i] = numPrev(num);
            ret.add(new String(array));
            array[i] = numSucc(num);
            ret.add(new String(array));
            array[i] = num;
        }
        return ret;
    }
}
//时间24 ms击败88.20%
//内存42 MB击败94.55%
class Solution {
    public int openLock(String[] deadends, String target) {
        // 思路:从0000开始,找相邻的密码,比如这个时候就有八个
        // 9000、1000
        // 0900、0100
        // 0090、0010
        // 0009、0001
        // 然后将这八个结果存入队列中,其中每个相邻的结果后面还会有八个
        // 当然要是在这期间遇上了是死亡数字或者是已经被访问过的,就不加入队列
     //(这有两种方法,前面的是第一种,从0000开始找target,还有一种就是从target和0000两个方向开始找,用两个set保存,当这个set的邻居结果在另一个set中,表示已找到)
// 以下采用第二种 Set<String> deadSet = new HashSet<>(); for(String deadStr : deadends) deadSet.add(deadStr); if(deadSet.contains("0000")) return -1; if("0000".equals(target)) return 0; Set<String> st1 = new HashSet<>();//用来记录从0000开始的令居 Set<String> st2 = new HashSet<>();//用来从target开始的邻居 Set<String> moved = new HashSet<>();//表示已经访问过的 st1.add("0000"); st2.add(target); int step = 0; while(!st1.isEmpty() && !st2.isEmpty()){ step++; if(st1.size() > st2.size()){ // 从短的开始找邻居,这样复杂度更低 Set<String> temp = st1; st1 = st2; st2 = temp; } Set<String> st3 = new HashSet<>();//用来记录新的邻居 for(String str : st1){ Set<String> neighbourSet = getNeighbour(str);//拿到了所有的子串 for(String neighbourStr : neighbourSet){ // 检查是否有符合条件的或者已经遍历过的 if(deadSet.contains(neighbourStr)) continue; if(st2.contains(neighbourStr)) return step;//当这个值出现在了两个set,则表示有重合,即出现过 if(!moved.contains(neighbourStr)){ st3.add(neighbourStr); moved.add(neighbourStr); } } } st1 = st3; } // 没找到则返回-1 return -1; } public Set<String> getNeighbour(String str){ Set<String> st3 = new HashSet<>(); StringBuilder sb = new StringBuilder(); sb.append(str); char[] ch = str.toCharArray(); for(int i = 0; i < str.length(); i++){ char curC = ch[i]; // 向下转 curC = ch[i] == '0' ? '9' : (char)(ch[i] - 1); sb.setCharAt(i,curC); st3.add(sb.toString()); // 向上转 curC = ch[i] == '9' ? '0' : (char)(ch[i] + 1); sb.setCharAt(i,curC); st3.add(sb.toString()); sb.setCharAt(i,ch[i]); } return st3; } }
//时间13 ms击败99.55%
//内存42.2 MB击败90.19%
class Solution {
    // 双向bfs
    public int openLock(String[] deadends, String target) {
        if (target.equals("0000"))
            return 0;  // 如果目标就是初始状态,不需要旋转锁,直接返回 0
        HashSet<String> ds = new HashSet<>();  // 存放所有不可达的状态,即不能旋转到的状态
        for (String d : deadends)
            ds.add(d);  // 将不可达状态加入到集合 ds 中
        if (ds.contains("0000"))
            return -1;  // 如果初始状态是不可达状态,那么无法打开锁,直接返回 -1
        HashSet<String> visited = new HashSet<>();
        visited.add("0000");
        HashSet<String> beginVisited = new HashSet<>();
        beginVisited.add("0000");
        HashSet<String> endVisited = new HashSet<>();
        endVisited.add(target);
        int step = 0;
        while (!beginVisited.isEmpty() && !endVisited.isEmpty()) {
            if (beginVisited.size() > endVisited.size()) {
                HashSet<String> temp = beginVisited;
                beginVisited = endVisited;
                endVisited = temp;
            }
            HashSet<String> nextVisited = new HashSet<>();
            for (String str : beginVisited) {
                if (curr(str, endVisited, visited, nextVisited, ds)) {
                    return step + 1;
                }
            }
            beginVisited = nextVisited;
            step++;
        }
        return -1;
    }

    //可能的变化
    public boolean curr(String word, HashSet<String> endVisited, HashSet<String> visited, HashSet<String> nextVisited, HashSet<String> wordset) {
        char[] chars = word.toCharArray();
        for (int i = 0; i < chars.length; i++) {  // 遍历当前状态中的每个数字
            char num = chars[i];  // 获取当前数字
            // 将当前数字向上转动一格
            chars[i] = num == '9' ? '0' : (char) (num + 1);  // 如果当前数字是 9,那么向上转动一格后变成 0;否则加 1
            String str1 = new String(chars);
            if (!wordset.contains(str1)) {
                if (endVisited.contains(str1)) {
                    return true;
                }
                if (!visited.contains(str1)) {
                    nextVisited.add(str1);
                    visited.add(str1);
                }
            }
            // 将当前数字向下转动一格
            chars[i] = num == '0' ? '9' : (char) (num - 1);  // 如果当前数字是 0,那么向下转动一格后变成 9;否则减 1
            String str2 = new String(chars);
            if (!wordset.contains(str2)) {
                if (endVisited.contains(str2)) {
                    return true;
                }
                if (!visited.contains(str2)) {
                    nextVisited.add(str2);
                    visited.add(str2);
                }
            }
            chars[i] = num;  // 将当前数字重置为原来的值,为下一次循环做准备
        }
        return false;
    }
}
//时间10 ms击败99.85%
//内存41.8 MB击败96.73%
class Solution {
    private static final int START = 0;
    private static final int[] WHEELS = {1000, 100, 10, 1};
    private static final int[] DIRECTIONS = {-1, 1};

    public int openLock(String[] deadends, String target) {
        boolean[] dead = new boolean[10000];
        for (String deadend : deadends) {
            dead[Integer.parseInt(deadend)] = true;
        }
        if (dead[START]) {
            return -1;
        }

        int fin = Integer.parseInt(target);
        if (fin == START) {
            return 0;
        }

        int count = 0;
        dead[START] = dead[fin] = true;
        Set<Integer> bfs1 = new HashSet<>(Set.of(START));
        Set<Integer> bfs2 = new HashSet<>(Set.of(fin));
        while (!bfs1.isEmpty() && !bfs2.isEmpty()) {
            ++count;
            if (bfs1.size() > bfs2.size()) {
                Set<Integer> tmp = bfs1;
                bfs1 = bfs2;
                bfs2 = tmp;
            }

            Set<Integer> nextBfs1 = new HashSet<>();
            for (int curr : bfs1) {
                for (int wheel : WHEELS) {
                    for (int direction : DIRECTIONS) {
                        int currDigit = curr / wheel % 10;
                        int nextDigit = (currDigit + direction + 10) % 10;
                        int next = curr + (nextDigit - currDigit) * wheel;
                        if (bfs2.contains(next)) {
                            return count;
                        } else if (dead[next]) {
                            continue;
                        }
                        dead[next] = true;
                        nextBfs1.add(next);
                    }
                }
            }
            bfs1 = nextBfs1;
        }
        return -1;
    }
}

 

posted @ 2023-04-13 12:13  忧愁的chafry  阅读(17)  评论(0编辑  收藏  举报