leetcode之路(207.課程表)本文詳細介紹有向圖的拓撲排序

leetcode之路(207.課程表)

本文詳細介紹有向圖的拓撲排序

207.課程表 題目:

現在你總共有 n 門課需要選,記爲 0 到 n-1。

在選修某些課程之前需要一些先修課程。 例如,想要學習課程 0 ,你需要先完成課程 1 ,我們用一個匹配來表示他們: [0,1]

給定課程總量以及它們的先決條件,判斷是否可能完成所有課程的學習?

示例 1:

輸入: 2, [[1,0]]
輸出: true
解釋: 總共有 2 門課程。學習課程 1 之前,你需要完成課程 0。所以這是可能的。 示例
示例 2:
輸入: 2, [[1,0],[0,1]]
輸出: false
解釋: 總共有 2 門課程。學習課程 1 之前,你需要先完成​課程0;並且學習課程 0 之前,你還應先完成課程 1。這是不可能的。
說明:

輸入的先決條件是由邊緣列表表示的圖形,而不是鄰接矩陣。詳情請參見圖的表示法。 你可以假定輸入的先決條件中沒有重複的邊

來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/course-schedule
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

“實現一個功能如果需要10分鐘,思考的時間是7分鐘”
思考過程
題意就是給定一個整數n,和一系列的關係數組【a,b】;n表示一共有n門課程;在後面的每一個數組中:a是b的先行課(必須先學課程a,才能學習課程b),就像是必須先學習了編程語言,才能學數據結構;可以將每一個關係數組【a,b】看成是一條有向弧,a->b;題目的要求是** 讓判斷是否可以完成所有課程,那麼我們思考什麼時候不能完成所有課程呢?**題目中有當關係數組爲[[0,1][1,0]]的時候由於0和1互爲對方的先行課,導致矛盾,無法完成這兩門課程。進一步思考,不難發現只要我們用關係數組構成的有向圖是有環的就無法完成所有課程,由此,此問題便轉換成了判斷有向圖是否有環的問題。對於此類問題,我們可以用拓撲排序來檢測,從而輕鬆解決。下面介紹拓撲排序的方法:

拓撲排序

對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological
Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。
–百度詞條

拓撲排序藉助一個存有各個點入度的數組和一個n個數組,第i(0<=i<n)個數組存有以第i個點爲弧尾(沒有箭頭的那端)的點的序號(也可以用鏈表結構);
結合這個題目,一開始就可以上的課一定是沒有先行課的(有向圖上一開始入讀就爲0的點),後續的可以上的課一定是先行課都已經上過了的課(有向圖上去掉已經上過的課所對應頂點後,入度爲0的點);
舉個例子:輸入:n=5,[[1,2],[1,3],[2,3],[3,4]],[4,5],[5,3]]
我們可以得到圖1,並求得每個點的入度存入數組in,此時入度爲0的點只有點1,於是我們去掉點1,並把以1爲弧尾的弧所指的點的度數減1,得到圖2,然後我們發現點2的度數變爲了0,於是又可以按照去掉點1的方法去掉點2,得到圖三。

圖1:圖1
圖2:
圖二
圖3:
在這裏插入圖片描述
然後發現沒有新出現入度爲0的點了,拓撲排序就結束了,我們可以記錄下排序好的點的個數,如果排好的點的個數與點的總個數相同則該有向圖不含有環,反之,該圖有環。
在編程過程中我們一般用隊列或者棧暫時的存儲入度爲0的點,每pop()出一個點,並使cnt++(用來計算排好點的個數),就更新in數組,同時把更新後入度新變爲0的點存入棧或隊列中等待下一步pop();
以上就是這個拓撲排序的思想。
附上代碼

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites){
    //計算各點入度,並存入數組in中
        vector<int> in(numCourses,0);				
        for(int i=0;i<prerequisites.size();i++){
            in[prerequisites[i][1]]++;
        }
        //把開始時入度即爲0的點入棧
        stack<int> st;
        for(int i=0;i<in.size();i++){
            if(in[i]==0) st.push(i);
        }
        //next數組用以儲存各點所指的點
        vector<vector<int>> next(numCourses);
        for(int i=0;i<prerequisites.size();i++){
            next[prerequisites[i][0]].push_back(prerequisites[i][1]);
        }
        int cnt=0while(!st.empty()){
            int i=st.top();
            st.pop();
            cnt++;
            //把以點i爲弧尾的弧所指的點的入度減1,若爲0,則入棧;
            for(int j=0;j<next[i].size();j++){
                if(--in[next[i][j]]==0) st.push(next[i][j]);
            }
            }
            //最後判斷排好序的點的個數是否與總個數相等,若相等,則該圖無環,反之則有。
        if(cnt==numCourses) return true;
        return false;
    }
};

提交結果

執行結果:通過
執行用時 :16 ms, 在所有 cpp 提交中擊敗了99.60%的用戶
內存消耗 :12.8 MB, 在所有 cpp
提交中擊敗了19.56%的用戶

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章