Leetcode752題-開啟轉盤鎖問題(BFS求解)
首先介紹一下題目,網址如右:https://leetcode-cn.com/problems/open-the-lock/
你有一個帶有四個圓形撥輪的轉盤鎖。每個撥輪都有10個數字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每個撥輪可以自由旋轉:例如把 '9' 變為 '0','0' 變為 '9' 。每次旋轉都只能旋轉一個撥輪的一位數字。
鎖的初始數字為 '0000' ,一個代表四個撥輪的數字的字串。
列表 deadends 包含了一組死亡數字,一旦撥輪的數字和列表裡的任何一個元素相同,這個鎖將會被永久鎖定,無法再被旋轉。
字串 target 代表可以解鎖的數字,你需要給出最小的旋轉次數,如果無論如何不能解鎖,返回 -1。
為什麼會想到利用BFS,即廣度優先搜尋解決?首先分析題目,密碼鎖每處於一個狀態時,下一次都有八種可能的狀態去轉換(每一位都有兩種變化),可以將每一個字串狀態看成是樹的一個節點,下面都連線著八個後繼節點。
那麼好,問題轉化成樹了,緊接著就會想到深度優先搜尋(DFS)和廣度優先搜尋(BFS),為什麼用BFS呢,因為題目中求得是“最小旋轉次數”,一旦涉及到最小的問題都應該想到用BFS求解。因為對於樹來說,BFS是一層一層的去遍歷,一旦在某一層找到結果就會直接返回,而DFS是一次搜尋完一個分支,最終搜尋完所有分支才得到結果,所以只要最優解不在樹的最後一層,那麼BFS所用的時間就會比DFS更少(因為訪問的節點數更少).
好了,既然解法有了,應該使用BFS,具體應該怎麼做呢?
BFS儲存的資料結構最好用佇列實現,佇列裡每次儲存的是一層的樹節點,訪問完一層就將其全部推出繼續加入下一層。一般來說,這類BFS求解的題目都有一個模板,大致如下:
int solution(){ Queue<XX> qu=new LinkedList<>(); qu.offer(初始XX); //初始值輸入 visited[];//儲存已經訪問過的狀態,按照情況選擇布林陣列或者set集合儲存 int res=0; //初始化結果 while(!qu.isEmpty()){ int sz=qu.size(); for(int i=0;i<sz;i++){ XX xx=qu.poll(); 訪問xx的後繼狀態,如果visited包含它或者有其他限制,則continue; 否則將其加入qu; 同時加入visited中; } res++; //這個時候res自增 } return res; }
模板已經有了,稍微思考一下就能得到下面的解法:
public int openLock(String[] deadends, String target) { Queue<String> qu=new LinkedList<>(); qu.offer("0000");//初始狀態入隊 Set<String> vis=new HashSet<>();//儲存已經到達過的狀態 Set<String> dead=new HashSet<>(); //儲存死亡狀態 for(String s:deadends) dead.add(s); int res=0; //儲存步數 while(!qu.isEmpty()){ int sz=qu.size(); for(int i=0;i<sz;i++){ String ss=qu.poll(); if(dead.contains(ss)) continue; if(ss.equals(target)) return res; for(int j=0;j<4;j++){ //關於後繼狀態如何增加,不同的題目不一樣,要學會轉化條件 String s1=upper(ss,j); String s2=down(ss,j); if(!vis.contains(s1)){ vis.add(s1); qu.offer(s1); } if(!vis.contains(s2)){ vis.add(s2); qu.offer(s2); } } } res++; } return -1; } //轉盤鎖順時針旋轉 String upper(String s,int idx){ char[] c=s.toCharArray(); //注意如何完成String與char陣列的互相轉化,以後可能還會用到 if(c[idx]=='9') c[idx]='0'; else c[idx]++; return new String(c); } //轉盤鎖逆時針旋轉 String down(String s,int idx){ char[] c=s.toCharArray(); if(c[idx]=='0') c[idx]='9'; else c[idx]--; return new String(c); }
該解法確實通過了Leetcode所有案例,雖然或許時間複雜度不是最優,但是應該是最為普遍,最廣為適用的解法了。
以上就是我對於這一題的解法,如有不足請在評論區批評指正。