打开转盘锁

26 篇文章 0 订阅
7 篇文章 0 订阅

题干

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有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

源码

//使用广度优先搜索
public class Open_dial_lock 
{
	private int stepArr[][] = {{0,0,0,1},{0,0,1,0},{0,1,0,0},{1,0,0,0},{0,0,0,-1},{0,0,-1,0},{0,-1,0,0},{-1,0,0,0}};//方向数组
	private int visit[][][][] =new int [10][10][10][10];
	//Set <String> set=new HashSet<String>();
	public int openLock(String[] deadends, String target)
	{
		int len=deadends.length;
        for (int j1 = 0; j1 < len; j1++) //?????????????????
        {
			String string1 = deadends[j1];
			int a = Integer.parseInt(string1.substring(0, 1));
        	int b = Integer.parseInt(string1.substring(1, 2));
        	int c = Integer.parseInt(string1.substring(2, 3));
        	int d = Integer.parseInt(string1.substring(3, 4));
        	visit[a][b][c][d]=1;
		}
        
	    Queue <String> queue=new LinkedList<String>();  // store all nodes which are waiting to be processed
	    int step = 0;       // number of steps neeeded from root to current node
	    // initialize
	    queue.add("0000");
		if(visit[0][0][0][0]==1)
			return -1;
	    visit[0][0][0][0]=1;
	    
	    // BFS
	    while (!queue.isEmpty()) 
	    {
	        step = step + 1;
	        // iterate the nodes which are already in the queue
	        int size = queue.size();
	        for (int i = 0; i < size; ++i)
	        {
	            String cur =queue.peek();
	            if(cur.equals(target))
	            	return step-1; 
	            for (int j=0;j<8;j++) 
	            {//这里常用一个二维方向数组实现
	            	int a = Integer.parseInt(cur.substring(0, 1))+stepArr[j][0];
	            	int b = Integer.parseInt(cur.substring(1, 2))+stepArr[j][1];
	            	int c = Integer.parseInt(cur.substring(2, 3))+stepArr[j][2];
	            	int d = Integer.parseInt(cur.substring(3, 4))+stepArr[j][3];
	            	if(a==-1)
	            		a=9;
	            	if(b==-1)
	            		b=9;
	            	if(c==-1)
	            		c=9;
	            	if(d==-1)
	            		d=9;
	            	if(a>9 || b>9 || c>9 || d>9)
	            		continue;
	            	String string=a+""+b+""+c+""+d+"";
		            if(visit[a][b][c][d]==0)
		            {
		            	queue.add(string);
		            	visit[a][b][c][d]=1;
		            }
		            	
	            }
	            queue.poll();
	        }
	    }
	    return -1;          // there is no path from root to target,目标在deadends内
	}
}
//LeetCode上最快的源码
class Solution 
{
    public int openLock(String[] deadends, String target) 
    {
        boolean[] isVisit=new boolean[10000];
        boolean[] isDead=new boolean[10000];
        for (String deadEnd : deadends) 
        {//isDead[a]=true,表明a这个序列会锁死
            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;
    }
	/*neighbor函数用来生成code序列的相邻序列,例如code等于0000,返回的数组中的八个元素分别为
	9 1 90 10 900 100 9000 1000 (前面自动补零),分别是第一个旋钮到第四个旋钮前后各拧两下,
	得到4*2=8个元素*/
    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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值