LeetCode——課程表I&II

NO.207 課程表 中等 、NO.210 課程表 II 中等

非常經典的拓撲排序問題

NO.207 課程表 中等

Ygz9p9.png

YgzSfJ.png

思路一:拓撲排序 拓撲排序的總體思路選擇 廣度優先遍歷+貪心

通常拓撲排序有兩個主要功能:

  1. 得到一條拓撲序列,拓撲序列不唯一。
  2. 判斷一個有向圖是否有環。

實現上,用兩個集合,分別保存每個節點入度的數量、每個節點的下一個後繼節點。還需要一個隊列,進行廣度優先遍歷。

將沒有前驅的節點(入度爲 0 )入隊,廣搜每出隊一個節點,就將該節點的所有後繼鄰接節點的入度 -1 ,如果入度減爲 0(沒有前驅節點) 則節點入隊。

廣搜過程中記錄出隊節點的數量 count,如果圖中有環,則 count<numCourses

public boolean canFinish(int numCourses, int[][] prerequisites) {
    if (prerequisites.length == 0) return true;
    //保存每個節點的入度
    int[] inDegree = new int[numCourses];
    //保存每個節點的下一個後繼鄰接節點
    HashSet<Integer>[] nextAdjacency = new HashSet[numCourses];
    for (int i = 0; i < numCourses; i++) nextAdjacency[i] = new HashSet<>();
    //預處理
    for (int[] temp : prerequisites) {
        inDegree[temp[0]]++;
        nextAdjacency[temp[1]].add(temp[0]);
    }
    //廣搜隊列
    Queue<Integer> queue = new LinkedList<>();
    //入度爲 0 的節點入隊
    for (int i = 0; i < inDegree.length; i++) {
        if (inDegree[i] == 0) {
            queue.offer(i);
        }
    }
    //開始廣搜,記錄出過隊的節點數量
    int count = 0;
    while (!queue.isEmpty()) {
        Integer poll = queue.poll();
        count++;
        //遍歷出隊節點的所有後繼鄰接節點
        for (int next : nextAdjacency[poll]) {
            //減少後繼節點的一條入度
            inDegree[next]--;
            //如果後繼節點的入度爲 0 則入隊
            if (inDegree[next] == 0) {
                queue.offer(next);
            }
        }
    }
    //如果存在環,則 count<numCourses
    return count == numCourses;
}

時間複雜度:O(E+V) 邊的數量 E ,節點數量 V。兩次遍歷鄰接表

空間複雜度:O(E+V)

NO.210 課程表 II 中等

在這裏插入圖片描述YgzETO.png

相較於前一題,本題需要返回一條拓撲排序的路徑。

思路一:拓撲排序 和上一題的思路一樣,區別在於本題需要記錄出隊的節點序列,而不僅僅是數量。

public int[] findOrder(int numCourses, int[][] prerequisites) {
    //保存每個節點的入度
    int[] inDegree = new int[numCourses];
    //保存每個節點的後繼鄰接節點
    List<Integer>[] nextAdjacency = new List[numCourses];
    //預處理
    for (int i = 0; i < numCourses; i++) nextAdjacency[i] = new LinkedList<>();
    for (int[] temp : prerequisites) {
        inDegree[temp[0]]++;
        nextAdjacency[temp[1]].add(temp[0]);
    }
    //廣搜隊列
    Queue<Integer> queue = new LinkedList<>();
    //入度爲 0 的節點入隊
    for (int i = 0; i < inDegree.length; i++) {
        if (inDegree[i] == 0) {
            queue.offer(i);
        }
    }
    //保存出隊節點序列 和 數量
    int[] res = new int[numCourses];
    int count = 0;
    //廣搜
    while (!queue.isEmpty()) {
        int poll = queue.poll();
        res[count++] = poll;
        //遍歷出隊節點的所有後繼鄰接節點
        for (int next : nextAdjacency[poll]) {
            //後繼鄰接節點入度-1
            inDegree[next]--;
            //後繼鄰接節點入度爲 0 則入隊
            if (inDegree[next] == 0) {
                queue.offer(next);
            }
        }
    }
    //是否有環
    return count == numCourses ? res : new int[0];
}

時間複雜度:O(E+V) 邊的數量 E ,節點數量 V。兩次遍歷鄰接表

空間複雜度:O(E+V)


本人菜鳥,有錯誤請告知,感激不盡!

更多題解和源碼:github

空間複雜度:O(E+V)


本人菜鳥,有錯誤請告知,感激不盡!

更多題解和源碼:github

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