36、打开转盘锁

题目描述:
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。

锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。

列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。

字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。

示例 1:

输入:deadends = [“0201”,“0101”,“0102”,“1212”,“2002”], target = “0202”
输出:6
解释:
可能的移动序列为 “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”。
注意 “0000” -> “0001” -> “0002” -> “0102” -> “0202” 这样的序列是不能解锁的,
因为当拨动到 “0102” 时这个锁就会被锁定。
示例 2:

输入: deadends = [“8888”], target = “0009”
输出:1
解释:
把最后一位反向旋转一次即可 “0000” -> “0009”。
示例 3:

输入: deadends = [“8887”,“8889”,“8878”,“8898”,“8788”,“8988”,“7888”,“9888”], target = “8888”
输出:-1
解释:
无法旋转到目标数字且不被锁定。
示例 4:

输入: deadends = [“0000”], target = “8888”
输出:-1

提示:

死亡列表 deadends 的长度范围为 [1, 500]。
目标数字 target 不会在 deadends 之中。
每个 deadends 和 target 中的字符串的数字会在 10,000 个可能的情况 ‘0000’ 到 ‘9999’ 中产生。
很明显使用的是BFS,跟之前的两个岛屿最短桥相连接是一个道理
定义一个HashSet是为了防止出现死循环,即将已经遍历过的放入,如果重复则不放入
注意的是如果"0000"在deadends那么直接返回-1,如果target等于"0000"那么返回0

class Solution {
    public int openLock(String[] deadends, String target) {
         //用以表示方向的数组
		Set<String> temSet = new HashSet<String>();
		int direc[][] = {{1,0,0,0},
						 {-1,0,0,0},
						 {0,1,0,0},
						 {0,-1,0,0},
						 {0,0,1,0},
						 {0,0,-1,0},
						 {0,0,0,1},
						 {0,0,0,-1}};
		List<String> dead = Arrays.asList(deadends);
		String tem = "0000";//从0000开始
		Queue<String> duilie = new LinkedList<>();
		if(dead.contains(tem)){
			return -1;
		}
        if("0000".equals(target)){
			return 0;
		}
		duilie.offer(tem);
		temSet.add(tem);
		int result = 0;
		while (!duilie.isEmpty()) {
            result ++;

			int size = duilie.size();
			for (int i = 0; i < size; i++) {
				String s1 = duilie.poll();
				//八个方向都进行遍历
				for (int di[] : direc) {
					int i1 = (Integer.parseInt(s1.charAt(0)+"") + di[0] + 10) % 10;
					int i2 = (Integer.parseInt(s1.charAt(1)+"") + di[1] + 10) % 10;
					int i3 = (Integer.parseInt(s1.charAt(2)+"") + di[2] + 10) % 10;
					int i4 = (Integer.parseInt(s1.charAt(3)+"") + di[3] + 10) % 10;
					
					String getString = i1 + "" + i2 +"" + i3 + "" + i4;
					if(target.equals(getString)){
						return result;
					}
					if(!dead.contains(getString)){
						//这一步是为了防止死循环
						int tems = temSet.size();
						temSet.add(getString);
						if(temSet.size() > tems){
							duilie.offer(getString);
						}
					}
				}
			}
		
		}
		return -1;
    }
}

但是不得不说虽然能写出来,但是效率真滴低
可能是hashset导致的速度低

class Solution {
    public int openLock(String[] deadends, String target) {
        boolean[] isVisit=new boolean[10000];
        boolean[] isDead=new boolean[10000];
        for (String deadEnd : deadends) {
            int deadNum = Integer.parseInt(deadEnd);
            isDead[deadNum] = true;
        }
        //如果"0000"在deadEnds中,返回-1
        if(isDead[0]) {
            return -1;
        }
        if("0000".equals(target)) {
            return 0;
        }

        int step=0;


        Queue<Integer> queue=new ArrayDeque<>();
        queue.offer(Integer.parseInt(target));
        isVisit[Integer.parseInt(target)]=true;
        int last=Integer.parseInt(target);
        int cenglast=last;
        while(!queue.isEmpty())
        {
            while(true)
            {
                int[] neighbor=new int[8];
                int head=queue.poll();
                if(head==0) {
                    return step;
                }
                int[] nei=neighbor(head);
                for(int i=0;i<8;i++)
                {
                    int trans=nei[i];
                    if(isVisit[trans] || isDead[trans]) {
                        continue;
                    }
                    queue.offer(trans);
                    last=trans;
                    isVisit[trans]=true;
                }
                if(head==cenglast) {
                    break;
                }
            }
            step++;
            cenglast=last;
        }
        return -1;


    }

    private int[] neighbor(int code)
    {
        int[] res=new int[8];
        int a=code%10;
        int b=(code/10)%10;
        int c=(code/100)%10;
        int d=(code/1000)%10;
        res[0]=d*1000+c*100+b*10+(a+10-1)%10;
        res[1]=d*1000+c*100+b*10+(a+1)%10;
        res[2]=d*1000+c*100+((b+10-1)%10)*10+a;
        res[3]=d*1000+c*100+((b+1)%10)*10+a;
        res[4]=d*1000+((c+10-1)%10)*100+b*10+a;
        res[5]=d*1000+((c+1)%10)*100+b*10+a;
        res[6]=((d+10-1)%10)*1000+c*100+b*10+a;
        res[7]=((d+1)%10)*1000+c*100+b*10+a;
        return res;
    }
}

将HashSet改成了visited数组,发现还是很慢,不知道原因了

class Solution {
    public int openLock(String[] deadends, String target) {
    //用以表示方向的数组
		int direc[][] = {{1,0,0,0},
						 {-1,0,0,0},
						 {0,1,0,0},
						 {0,-1,0,0},
						 {0,0,1,0},
						 {0,0,-1,0},
						 {0,0,0,1},
						 {0,0,0,-1}};
		List<String> dead = Arrays.asList(deadends);
		String tem = "0000";//从0000开始
		Queue<String> duilie = new LinkedList<>();
		if(dead.contains(tem)){
			return -1;
		}
		if("0000".equals(target)){
			return 0;
		}
		duilie.offer(tem);
		int result = 0;
		
		//换成isvisit数组
		boolean isvisit[] = new boolean[10000];
		isvisit[0]  = true;
		while (!duilie.isEmpty()) {
			result ++;
			int size = duilie.size();
			for (int i = 0; i < size; i++) {
				String s1 = duilie.poll();
				//八个方向都进行遍历
				for (int di[] : direc) {
					int i1 = (Integer.parseInt(s1.charAt(0)+"") + di[0] + 10) % 10;
					int i2 = (Integer.parseInt(s1.charAt(1)+"") + di[1] + 10) % 10;
					int i3 = (Integer.parseInt(s1.charAt(2)+"") + di[2] + 10) % 10;
					int i4 = (Integer.parseInt(s1.charAt(3)+"") + di[3] + 10) % 10;
					//9999
					String getString = i1 + "" + i2 +"" + i3 + "" + i4;
					if(target.equals(getString)){
						return result;
					}
					if(!dead.contains(getString)){
						//这一步是为了防止死循环
						if(!isvisit[i1 * 1000 + i2 * 100 + i3 * 10 + i4]){
							isvisit[i1 * 1000 + i2 * 100 + i3 * 10 + i4] = true;
							duilie.offer(getString);
						}
					}
				}
			}
			
		}
		return -1;
    }
}

因此可以看看双向搜索,提高速度
双向广度优先搜索
代码实现如下

class Solution {
    public int openLock(String[] deadends, String target) {
         Set<String> dead = new HashSet<>(Arrays.asList(deadends));
    Set<String> visited = new HashSet<>();
    String init = "0000";
    if (dead.contains(init) || dead.contains(target)) {
        return -1;
    }
 
    if (target.equals(init)) {
        return 0;
    }
 
    Set<String> set1 = new HashSet<>();
    set1.add(init);
    Set<String> set2 = new HashSet<>();
    set2.add(target);
 
    int steps = 0;
    while (!set1.isEmpty() && !set2.isEmpty()) {
        if (set1.size() > set2.size()) {
            Set<String> temp = set1;
            set1 = set2;
            set2 = temp;
        }
 
        Set<String> set3 = new HashSet<>();
        for (String cur : set1) {
            for (String next : getNexts(cur)) {
                if (set2.contains(next)) {
                    return steps + 1;
                }
 
                if (!dead.contains(next) && !visited.contains(next)) {
                    visited.add(next);
                    set3.add(next);
                }
            }
        }
 
        steps++;
        set1 = set3;
    }
 
    return -1;

    }
    
private List<String> getNexts(String cur) {
    List<String> nexts = new LinkedList<>();
    for (int i = 0; i < cur.length(); ++i) {
        char ch = cur.charAt(i);
 
        char newCh = ch == '0' ? '9' : (char)(ch - 1);
        StringBuilder builder = new StringBuilder(cur);
        builder.setCharAt(i, newCh);
        nexts.add(builder.toString());
 
        newCh = ch == '9' ? '0' : (char)(ch + 1);
        builder = new StringBuilder(cur);
        builder.setCharAt(i, newCh);
        nexts.add(builder.toString());
    }
 
    return nexts;
}
}

尝试实现一遍,还是很低

class Solution {
    public int openLock(String[] deadends, String target) {
       //用以表示方向的数组
		int direc[][] = {{1,0,0,0},
						 {-1,0,0,0},
						 {0,1,0,0},
						 {0,-1,0,0},
						 {0,0,1,0},
						 {0,0,-1,0},
						 {0,0,0,1},
						 {0,0,0,-1}};
		List<String> dead = Arrays.asList(deadends);
		String tem = "0000";//从0000开始
		Queue<String> duilie = new LinkedList<>();
		Queue<String> duilie2 = new LinkedList<>();
		duilie2.offer(target);
		if(dead.contains(tem)){
			return -1;
		}
		if("0000".equals(target)){
			return 0;
		}
		duilie.offer(tem);
		int result = 0;
		
		//换成isvisit数组
		boolean isvisit[] = new boolean[10000];
		isvisit[0]  = true;
		while (!duilie.isEmpty() && !duilie2.isEmpty()) {
			//duilie是长度短的那个,duilie2是长度长的那个
			if(duilie.size() > duilie2.size()){
				Queue<String> tems = duilie;
				duilie = duilie2;
				duilie2 = tems;
			}
			int size = duilie.size();
			for (int i = 0; i < size; i++) {
				String s1 = duilie.poll();
				//八个方向都进行遍历
				for (int di[] : direc) {
					int i1 = (Integer.parseInt(s1.charAt(0)+"") + di[0] + 10) % 10;
					int i2 = (Integer.parseInt(s1.charAt(1)+"") + di[1] + 10) % 10;
					int i3 = (Integer.parseInt(s1.charAt(2)+"") + di[2] + 10) % 10;
					int i4 = (Integer.parseInt(s1.charAt(3)+"") + di[3] + 10) % 10;
					//9999
					String getString = i1 + "" + i2 +"" + i3 + "" + i4;
					if(duilie2.contains(getString)){
						return result + 1;
					}
					if(!dead.contains(getString)){
						//这一步是为了防止死循环
						if(!isvisit[i1 * 1000 + i2 * 100 + i3 * 10 + i4]){
							isvisit[i1 * 1000 + i2 * 100 + i3 * 10 + i4] = true;
							duilie.offer(getString);
						}
					}
				}
			}
			result ++;
		}
		return -1;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_34446716/article/details/87009961
今日推荐