【LeetCode】5410. 課程安排 IV Course Schedule IV (Python)


題目地址:https://leetcode-cn.com/problems/course-schedule-iv/

題目描述

你總共需要上 n 門課,課程編號依次爲 0n-1

有的課會有直接的先修課程,比如如果想上課程 0 ,你必須先上課程 1 ,那麼會以 [1,0] 數對的形式給出先修課程數對。

給你課程總數 n 和一個直接先修課程數對列表 prerequisite 和一個查詢對列表 queries

對於每個查詢對 queries[i] ,請判斷 queries[i][0] 是否是 queries[i][1] 的先修課程。

請返回一個布爾值列表,列表中每個元素依次分別對應 queries 每個查詢對的判斷結果。

注意:如果課程 a 是課程 b 的先修課程且課程 b 是課程 c 的先修課程,那麼課程 a 也是課程 c 的先修課程。

示例 1:

此處輸入圖片的描述

輸入:n = 2, prerequisites = [[1,0]], queries = [[0,1],[1,0]]
輸出:[false,true]
解釋:課程 0 不是課程 1 的先修課程,但課程 1 是課程 0 的先修課程。

示例 2:

輸入:n = 2, prerequisites = [], queries = [[1,0],[0,1]]
輸出:[false,false]
解釋:沒有先修課程對,所以每門課程之間是獨立的。

示例 3:

此處輸入圖片的描述

輸入:n = 3, prerequisites = [[1,2],[1,0],[2,0]], queries = [[1,0],[1,2]]
輸出:[true,true]

示例 4:

輸入:n = 3, prerequisites = [[1,0],[2,0]], queries = [[0,1],[2,0]]
輸出:[false,true]

示例 5:

輸入:n = 5, prerequisites = [[0,1],[1,2],[2,3],[3,4]], queries = [[0,4],[4,0],[1,3],[3,0]]
輸出:[true,false,true,false]

提示:

  1. 2 <= n <= 100
  2. 0 <= prerequisite.length <= (n * (n - 1) / 2)
  3. 0 <= prerequisite[i][0], prerequisite[i][3] < n
  4. prerequisite[i][0] != prerequisite[i][4]
  5. 先修課程圖中沒有環。
  6. 先修課程圖中沒有重複的邊。
  7. 1 <= queries.length <= 10^4
  8. queries[i][0] != queries[i][5]

題目大意

題目給出了一個圖。判斷是否可以從queries[i][0]走向queries[i][1]

解題方法

DFS

檢查有向圖中從queries[i][0]出發是否可以到達queries[i][1],最簡單的思路就是 DFS 看到能否搜索到。但是看了題目給出的數量級,估算如果每次query都在全圖 DFS 搜索,時間複雜度爲 O(queries.length * prerequisite.length) 約爲 10^8 量級,則會超時。

那麼 DFS 就不行了嗎?並不見得。我們可以看出 DFS 會存在同一路徑重複查找的現象,可以進行優化。

舉例說明,加入題目給出的先修課程的圖是這樣的:

1 -> 2 -> 3 -> 4

假如第一個 query 判斷了 1 -> 4 是可以的;
假如第二個 query 要判斷 2 -> 4,是否需要重新搜索一遍呢?我們在第一個query中已經走過了這條路了呀,就沒有必要重新搜索了。

即,我們的思路就是記錄已經判斷過的所有的路徑,防止重複計算。比如在上面的例子中,我們在搜索 1 -> 4 的過程中,保存記錄 1,2,3 都可以走到 4;如果下次再判斷 2 是否能到 4 的時候,就可以在O(1)的時間內直接出結果了。

代碼的實現時,先寫出普通的 DFS 搜索是否可從 start 到達 end 的代碼,然後可以用 Python3 提供的@functools.lru_cache,該函數能自動保存函數的參數和返回,相當於函數調用的記憶化。如果不用該函數,也可以自己定義memo數組來記錄參數和返回。

  • 時間複雜度:最好情況下只需要第一次搜索的時候把路徑保存下來,之後查表就行,因此時間複雜度是 O(n);最壞情況下,查詢的時候從來沒有走過重複的路徑(比如星型的圖),時間複雜度是O(N * queries.length)。
  • 空間複雜度:最省空間的時候是沒有保存過重複的路徑,空間複雜度是O(1);最費空間是把所有的節點兩兩路徑保存,空間複雜度是O(N^2)。

Python 代碼如下:

class Solution(object):
    def checkIfPrerequisite(self, n, prerequisites, queries):
        """
        :type n: int
        :type prerequisites: List[List[int]]
        :type queries: List[List[int]]
        :rtype: List[bool]
        """
        self.graph = collections.defaultdict(list)
        for pre in prerequisites:
            self.graph[pre[0]].append(pre[1])
        return [self.dfs(query[0], query[1]) for query in queries]
    
    # start -> end ?
    @functools.lru_cache
    def dfs(self, start, end):
        if start == end:
            return True
        return any(self.dfs(nxt, end) for nxt in self.graph[start])

歡迎關注負雪明燭的刷題博客,leetcode刷題800多,每道都講解了詳細寫法!

日期

2020 年 6 月 1 日 —— 6月的開始,兒童節快樂!

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