We have a list of bus routes. Each routes[i]
is a bus route that the i-th bus repeats forever. For example if routes[0] = [1, 5, 7]
, this means that the first bus (0-th indexed) travels in the sequence 1->5->7->1->5->7->1->... forever.html
We start at bus stop S
(initially not on a bus), and we want to go to bus stop T
. Travelling by buses only, what is the least number of buses we must take to reach our destination? Return -1 if it is not possible.git
Example: Input: routes = [[1, 2, 7], [3, 6, 7]] S = 1 T = 6 Output: 2 Explanation: The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.
Note:github
1 <= routes.length <= 500
.1 <= routes[i].length <= 500
.0 <= routes[i][j] < 10 ^ 6
.這道題給了咱們一堆公交線路表,而後給了起點和終點,問最少要換乘幾輛公交能夠從起點到達終點。這種本來只須要使用谷歌地圖或者百度地圖輕鬆實現的事,如今須要本身來實現。但這畢竟是簡化版,真實狀況必定要複雜得多。 這題容易進的一個誤區就是把 routes 直接看成鄰接鏈表來進行圖的遍歷,實際上是不對的,由於 routes 數組的含義是,某個公交所能到達的站點,而不是某個站點所能到達的其餘站點。這裏出現了兩種不一樣的結點,分別是站點和公交。而 routes 數組創建的是公交和其站點之間的關係,那麼應該將反向關係數組也創建出來,即要知道每一個站點有哪些公交能夠到達。因爲這裏站點的標號不必定是連續的,因此可使用 HashMap,創建每一個站點和其屬於的公交數組之間的映射。因爲一個站點能夠被多個公交使用,因此要用個數組來保存公交。既然這裏求的是最少使用公交的數量,那麼就相似迷宮遍歷求最短路徑的問題,BFS 應該是首先被考慮的解法。用隊列 queue 來輔助,首先將起點S排入隊列中,而後還須要一個 HashSet 來保存已經遍歷過的公交(注意這裏思考一下,爲啥放的是公交而不是站點,由於統計的是最少須要坐的公交個數,這裏一層就至關於一輛公交,最小的層數就是公交數),這些都是 BFS 的標配,應當已經很熟練了。在最開頭先判斷一下,若起點和終點相同,那麼直接返回0,由於根本不用坐公交。不然開始 while 循環,先將結果 res 自增1,由於既然已經上了公交,那麼公交個數至少爲1,初始化的時候是0。這裏使用 BFS 的層序遍歷的寫法,就是當前全部的結點都看成深度相同的一層,至於爲什麼採用這種倒序遍歷的 for 循環寫法,是由於以後隊列的大小可能變化,放在判斷條件中可能會出錯。在 for 循環中,先取出隊首站點,而後要去 HashMap 中去遍歷通過該站點的全部公交,若某個公交已經遍歷過了,直接跳過,不然就加入 visited 中。而後去 routes 數組中取出該公交的全部站點,若是有終點,則直接返回結果 res,不然就將站點排入隊列中繼續遍歷,參見代碼以下:數組
解法一:數據結構
class Solution { public: int numBusesToDestination(vector<vector<int>>& routes, int S, int T) { if (S == T) return 0; int res = 0; unordered_map<int, vector<int>> stop2bus; queue<int> q{{S}}; unordered_set<int> visited; for (int i = 0; i < routes.size(); ++i) { for (int j : routes[i]) { stop2bus[j].push_back(i); } } while (!q.empty()) { ++res; for (int i = q.size(); i > 0; --i) { int t = q.front(); q.pop(); for (int bus : stop2bus[t]) { if (visited.count(bus)) continue; visited.insert(bus); for (int stop : routes[bus]) { if (stop == T) return res; q.push(stop); } } } } return -1; } };
下面這種方法也是 BFS 解法,思路上跟上面的解法沒有啥大的區別,就是數據結構的寫法上略有不一樣。這裏的隊列 queue 放的是一個由站點和公交個數組成的 pair 對兒,這樣就不用維護一個全局的最小公交數變量了。固然反向關係數組仍是要創建出來的,即要知道每一個站點有哪些公交能夠到達。和上面稍有不一樣的是,使用了 HashSet 來保存通過某個站點的全部公交,但其實和用數組並沒啥區別,由於這裏沒有查詢需求,沒法發揮 HashSet 的優點。因爲對於每一個站點,都保存了當達該站點所需的最少公交數,那麼就不須要使用層序遍歷的 BFS 的寫法,直接用最通常的寫法便可。還有一個不一樣之處在於,這裏的 visited 保存的是遍歷過的站點,而再也不是公交了。在 while 循環中,首先將隊首元素取出來,這裏就取出來了當前站點 cur,和最少公交數 cnt,若當前站點就是終點,那就直接返回 cnt。不然遍歷通過當前站點的全部公交,對每輛公交,再去遍歷去全部站點,若站點已經被遍歷過了,直接跳過,不然就加入 visited 中,並和 cnt+1 一塊兒組成個 pair 對兒排入隊列中繼續遍歷,參見代碼以下:post
解法二:this
class Solution { public: int numBusesToDestination(vector<vector<int>>& routes, int S, int T) { if (S == T) return 0; unordered_map<int, unordered_set<int>> stop2bus; queue<pair<int, int>> q{{{S, 0}}}; unordered_set<int> visited; for (int i = 0; i < routes.size(); ++i) { for (int j : routes[i]) { stop2bus[j].insert(i); } } while (!q.empty()) { int cur = q.front().first, cnt = q.front().second; q.pop(); if (cur == T) return cnt; for (int bus : stop2bus[cur]) { for (int stop : routes[bus]) { if (visited.count(stop)) continue; visited.insert(stop); q.push({stop, cnt + 1}); } } } return -1; } };
Github 同步地址:url
https://github.com/grandyang/leetcode/issues/815spa
參考資料:code
https://leetcode.com/problems/bus-routes/
https://leetcode.com/problems/bus-routes/discuss/122712/Simple-Java-Solution-using-BFS
https://leetcode.com/problems/bus-routes/discuss/122771/C%2B%2BJavaPython-BFS-Solution