752. Open the Lock

1 题目理解

一个钟表有4个槽,每个槽可以停在0-9,10个状态。钟表每个槽的轮子可以转,例如可以从0转到9,也可以从0转到1。
钟表的起始状态是"0000"。每个数字代表一个槽的状态。
输入:字符串数组deadends,表示不能死亡状态,进入这个状态钟表就被锁住了,不能动了。输入字符串target表示想要达到的状态。
输出:到达最终状态的最少需要多少步。如果不能达到则为-1。
规则:每一步,钟表只能转动一个槽,只能转一下,
例子:
Input: deadends = [“0201”,“0101”,“0102”,“1212”,“2002”], target = “0202”
Output: 6
Explanation:
A sequence of valid moves would be “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”.
Note that a sequence like “0000” -> “0001” -> “0002” -> “0102” -> “0202” would be invalid,
because the wheels of the lock become stuck after the display becomes the dead end “0102”.

2 BFS

要求最短路径长度,所以使用BFS。
在某一种状态下,要先判断这个状态能不能动。如果deadends包含target,则不能达到。

在正常状态下,某一状态的变化,可以变化其中一个槽,变化的动作可以是加1,也可以是减1。变化之后进入新的状态。

    class Solution {
        public int openLock(String[] deadends, String target) {
            Set<String> set = new HashSet<String>();
            for(String str : deadends){
                set.add(str);
            }
            String start = "0000";
            if(set.contains(target) || set.contains(start)) return -1;
            
            Queue<String> queue = new LinkedList<String>();
            queue.offer(start);
            set.add(start);
            int step = 0;
            while(!queue.isEmpty()){
                int size = queue.size();
                //System.out.println(queue);
                for(int r=0;r<size;r++){
                    if(target.equals(queue.peek())){
                        return step;
                    }
                    char[] current = queue.poll().toCharArray();
                    for(int i=0;i<4;i++){
                        char ch = current[i];
                        int v = current[i]-48;
                        current[i] = (char)(((v+1+10)%10)+48);
                        String newState = new String(current);
                        if(!set.contains(newState)){
                            queue.offer(newState);
                            set.add(newState);
                        }
                        current[i] = (char)(((v-1+10)%10)+48);
                        String newState2 = new String(current);
                        if(!set.contains(newState2)){
                            queue.offer(newState2);
                            set.add(newState2);
                    
                        }
                        current[i] = ch;
                    }
                }
                step++;
            }
            return -1;
        }
    }

时间复杂度:可以选择的数据范围n=10,状态位数x=4, O ( 1 0 4 ∗ 4 ∗ 4 ) O(10^4*4*4) O(10444)。最坏情况下有 1 0 4 10^4 104种状态。每个状态可以有4*2=8种变化,所以是O(x)。枚举后得到新的状态,构建字符串,需要O(x)时间。所以最终结果是: O ( n x ∗ x ∗ x ) O(n^x*x*x) O(nxxx)

3 用int构建状态

因为每个位置的值在0-9之间,可以用4位二进制表示,我们可以用int表示状态,这样速度方面会更快。

class Solution {
    private final static int[] increments = new int[]{ 1, -1 };

    public int openLock(String[] deadends, String target) {
        int step = 0;
        Set<Integer> used = new HashSet<Integer>();
        String start = "0000";
        int startInt = 0;
        int targetInt = buildState(target);
        for(String deadend : deadends){
            used.add(buildState(deadend));
        }
        if(used.contains(startInt) || used.contains(targetInt)) return -1;
        used.add(startInt);
        Queue<Integer> queue = new LinkedList<Integer>();
        queue.offer(startInt);

        while(!queue.isEmpty()){
            int size =queue.size();
            for(int i=0;i<size;i++){
                int nodeInt = queue.poll();
                if(nodeInt==targetInt) return step;
                for(int increment : increments){
                    for(int j =0;j<4;j++){
                        int newNode = updateState(nodeInt,j,increment);
                        if(!used.contains(newNode)){
                            used.add(newNode);
                            queue.offer(newNode);
                        }
                    }
                }
            }

            step++;
        }

        return -1;
    }




    private int updateState(int state, int d, int inc) {
        int mask = (1 << 4) - 1;
        int[] num = new int[]{
                state & mask,
                (state >> 4) & mask,
                (state >> 8) & mask,
                (state >> 12) & mask
        };
        int n = num[d];
        if (n == 0 && inc == -1) {
            num[d] = 9;
        } else if (n == 9 && inc == 1) {
            num[d] = 0;
        } else {
            num[d] += inc;
        }
        int res = 0;
        for (int i = 3; i >= 0; i--) {
            res <<= 4;
            res |= num[i];
        }
        return res;
    }

    private int buildState(String state) {
        char[] c = state.toCharArray();
        int res = 0;
        for (int i = 0; i < c.length; i++) {
            int d = c[i] - '0';
            res <<= 4;
            res |= d;
        }
        return res;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值