[LeetCode] 815. Bus Routes 公交線路


 

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.

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.

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:

  • 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 對兒排入隊列中繼續遍歷,參見代碼如下:

 

解法二:

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 同步地址:

https://github.com/grandyang/leetcode/issues/815

 

參考資料:

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

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM