力扣(LeetCode) - 207 課程表(圖的深度優(yōu)先搜索||拓?fù)渑判颍?/h1>

本題就是判斷有向圖中是否有環(huán)枷踏,可以通過(guò)深度優(yōu)先搜索或拓?fù)渑判騺?lái)解決凿可。

一、題目

現(xiàn)在你總共有 n 門(mén)課需要選涉馅,記為 0 到 n-1归园。
在選修某些課程之前需要一些先修課程。 例如稚矿,想要學(xué)習(xí)課程 0 庸诱,你需要先完成課程 1 ,我們用一個(gè)匹配來(lái)表示他們: [0,1]
給定課程總量以及它們的先決條件盐捷,判斷是否可能完成所有課程的學(xué)習(xí)偶翅?
示例1
輸入: 2, [[1,0]]
輸出: true
解釋: 總共有 2 門(mén)課程。學(xué)習(xí)課程 1 之前碉渡,你需要完成課程 0聚谁。所以這是可能的。
示例2
輸入: 2, [[1,0],[0,1]]
輸出: false
解釋: 總共有 2 門(mén)課程滞诺。學(xué)習(xí)課程 1 之前形导,你需要先完成?課程 0;并且學(xué)習(xí)課程 0 之前习霹,你還應(yīng)先完成課程 1朵耕。這是不可能的。
說(shuō)明

  1. 輸入的先決條件是由邊緣列表表示的圖形淋叶,而不是鄰接矩陣阎曹。詳情請(qǐng)參見(jiàn)圖的表示法
  2. 你可以假定輸入的先決條件中沒(méi)有重復(fù)的邊煞檩。
    提示
  3. 這個(gè)問(wèn)題相當(dāng)于查找一個(gè)循環(huán)是否存在于有向圖中处嫌。如果存在循環(huán),則不存在拓?fù)渑判蛘迮龋虼瞬豢赡苓x取所有課程進(jìn)行學(xué)習(xí)熏迹。
  4. 通過(guò) DFS 進(jìn)行拓?fù)渑判?/a> - 一個(gè)關(guān)于Coursera的精彩視頻教程(21分鐘),介紹拓?fù)渑判虻幕靖拍睢?/li>
  5. 拓?fù)渑判蛞部梢酝ㄟ^(guò) BFS 完成凝赛。

二注暗、思路

解決這道題需要圖的知識(shí)坛缕,詳見(jiàn)這篇博客數(shù)據(jù)結(jié)構(gòu)-圖(定義、存儲(chǔ)結(jié)構(gòu)捆昏、遍歷赚楚、求最短路徑、拓?fù)渑判颍?/a>屡立。

我們可以通過(guò)輸入的課程信息構(gòu)成一個(gè)有向圖直晨。比如,示例1中膨俐,輸入2, [[1,0]]勇皇,表明有向圖中有兩個(gè)結(jié)點(diǎn)(0和1),有向圖中有一條环俅獭(0 -> 1)敛摘。因此,我們可以使用鄰接矩陣或者鄰接表構(gòu)造一個(gè)有向圖乳愉。

能完成所有課程兄淫,也就是課程完成的前提條件中沒(méi)有循環(huán)的關(guān)系。比如完成課程A需要先完成課程B蔓姚,完成課程B需要先完成課程A捕虽,這樣就形成了一個(gè)循環(huán),由結(jié)點(diǎn)A坡脐、B和他們之前的關(guān)系構(gòu)成的有向圖中就存在環(huán)泄私。

因此,如果課程能夠全部完成备闲,課程構(gòu)成的有向圖中就一定沒(méi)有環(huán)晌端。

如何判斷有向圖中有沒(méi)有環(huán)呢?可以用兩種辦法恬砂,一個(gè)是深度優(yōu)先搜索咧纠,通過(guò)深度優(yōu)先搜索來(lái)查找是否有環(huán)存在;一個(gè)是拓?fù)渑判蛐褐瑁邢驁D圖的拓?fù)渑判蛐蛄械某潭鹊扔诮Y(jié)點(diǎn)總數(shù)時(shí)漆羔,有向圖中就不存在環(huán)(反之就存在環(huán))。

拓?fù)渑判蚴褂绵徑颖肀容^方便狱掂。先構(gòu)造有向圖的鄰接表演痒,然后進(jìn)行拓?fù)渑判颍詈笈袛嗤負(fù)湫蛄虚L(zhǎng)度是不是等于結(jié)點(diǎn)總數(shù)符欠。

深度優(yōu)先搜索用鄰接矩陣表示圖比較方便嫡霞,因此我們先構(gòu)造圖的鄰接矩陣瓶埋,然后使用遞歸進(jìn)行深度優(yōu)先搜索希柿,使用HashSet來(lái)判斷路徑中是否環(huán)的存在诊沪。

三、代碼

3.1 拓?fù)渑判?/h4>
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        /**
         * 構(gòu)建鄰接表
        */
        EdgeNode[] edges = new EdgeNode[numCourses]; 
        Node temp = null;
        int topoSize = 0;
        for(int i=0;i<numCourses;i++){
            edges[i] = new EdgeNode();
            edges[i].in = 0;
            edges[i].val = i;
        }
        for(int i=0;i<prerequisites.length;i++){
            temp = edges[prerequisites[i][1]].next;
            Node newNode = new Node();
            newNode.val = prerequisites[i][0];
            edges[prerequisites[i][1]].next = newNode;
            newNode.next = temp;
            edges[prerequisites[i][0]].in ++;
        }
        /**
         * 進(jìn)行拓?fù)渑判?        */
        Stack<EdgeNode> stack = new Stack<EdgeNode>();//存儲(chǔ)入度為0的結(jié)點(diǎn)
        for(int i=0;i<numCourses;i++){                //將入度為0的結(jié)點(diǎn)壓入棧中
            if(edges[i].in==0){
                stack.push(edges[i]);
            }
        }
        EdgeNode deletedNode = null;
        while(!stack.isEmpty()){                      
            topoSize++;
            deletedNode = stack.pop();               //刪除入讀為0的結(jié)點(diǎn)
            temp = deletedNode.next;
            while(temp!=null){                       //更新其鄰接點(diǎn)的入度
                if(edges[temp.val].in>0){
                    edges[temp.val].in --;
                    if(edges[temp.val].in == 0)      //如果更新后的鄰接結(jié)點(diǎn)的入度為0曾撤,將其壓入棧中
                        stack.push(edges[temp.val]);
                }
                temp = temp.next;
            }
        }
        return topoSize == numCourses;
    }
    
    class EdgeNode{                     //鄰接表的邊結(jié)點(diǎn)
        int in;
        int val;
        Node next;
    }
    
    class Node{                            //鄰接結(jié)點(diǎn)
        int val;
        Node next;
    }

3.2 深度優(yōu)先搜索

    HashSet<Integer> set = new HashSet<Integer>();
    boolean[][] adjMat;
    boolean[] visited;
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        adjMat = new boolean[numCourses][numCourses];
        visited = new boolean[numCourses];
         /**
          * 構(gòu)建鄰接矩陣
         */
        for(int i=0;i<prerequisites.length;i++){
            adjMat[prerequisites[i][1]][prerequisites[i][0]] = true;
        }
        /**
          * 深度優(yōu)先搜索
         */
        for(int i=0;i<numCourses;i++){
            if(!visited[i]){
                set.clear();
                if(!DFS(i))
                    return false;
            }
        }
        return true;
    }
    
    private boolean DFS(int index){
        visited[index] = true;
        set.add(index);
        for(int i=0;i<visited.length;i++){
            if(adjMat[index][i]&&set.contains(i))
                return false;
            if(!visited[i]&&adjMat[index][i]){
                if(!DFS(i))
                    return false;
            }
        }
        set.remove(index);
        return true;
    }

  • 序言:七十年代末端姚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子挤悉,更是在濱河造成了極大的恐慌渐裸,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件装悲,死亡現(xiàn)場(chǎng)離奇詭異昏鹃,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诀诊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)洞渤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人属瓣,你說(shuō)我怎么就攤上這事载迄。” “怎么了抡蛙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵护昧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我粗截,道長(zhǎng)惋耙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任慈格,我火速辦了婚禮怠晴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浴捆。我一直安慰自己蒜田,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布选泻。 她就那樣靜靜地躺著冲粤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪页眯。 梳的紋絲不亂的頭發(fā)上梯捕,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音窝撵,去河邊找鬼傀顾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碌奉,可吹牛的內(nèi)容都是我干的短曾。 我是一名探鬼主播寒砖,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嫉拐!你這毒婦竟也來(lái)了哩都?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤婉徘,失蹤者是張志新(化名)和其女友劉穎漠嵌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盖呼,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡儒鹿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了几晤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挺身。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锌仅,靈堂內(nèi)的尸體忽然破棺而出章钾,到底是詐尸還是另有隱情,我是刑警寧澤热芹,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布贱傀,位于F島的核電站,受9級(jí)特大地震影響伊脓,放射性物質(zhì)發(fā)生泄漏府寒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一报腔、第九天 我趴在偏房一處隱蔽的房頂上張望株搔。 院中可真熱鬧,春花似錦纯蛾、人聲如沸纤房。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炮姨。三九已至,卻和暖如春碰煌,著一層夾襖步出監(jiān)牢的瞬間舒岸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工芦圾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛾派,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像洪乍,于是被迫代替她去往敵國(guó)和親梭依。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容