---
# System prepended metadata

title: '**Leetcode筆記(Course Schedule)**'
tags: [值得再想, graph, medium, leetcode]

---

# **Leetcode筆記(Course Schedule)**


:::info
:information_source: 題目 : Course Schedule, 類型 : graph , 等級 : medium
日期 : 2023/04/09，2023/05/16，2023/07/14，2023/10/27，2023/12/08，2024/02/14，2024/09/20，2024/10/11
:::


### 嘗試
2023/05/16
```python
再visit兩次 = 形成迴圈
只要一旦不符合dfs 就回傳錯誤


class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        record = collections.defaultdict(list)
        visited = set()
        for cor, pre in prerequisites:
            record[cor].append(pre)
        
        def dfs(c):
            if record[c] == []:
                return True
            if c in visited:
                return False
            
            visited.add(c)
            for p in record[c]:
                if not dfs(p):
                    return False
    
            # 通過
            record[c] = []        
            return True
        
        for c in range(numCourses):
            if not dfs(c):
                return False
        return True
```
2023/07/14
只會有一個錯誤的條件，(以dfs為例)如果在dfs過程中，**碰到正在visited中的課程**(也就是在dfs這條路上的課程)，必定錯誤，因為代表這條路有互相引用的情況
就是要遍歷每一個課程，然後**不斷檢查這門課程前面所有的pre課程**，任何衝突都沒有發生就可以回傳True
這類**if not dfs()就回傳False，代表是"嚴格dfs"**，如果有任何一階回傳False，所有都會變False
```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        graph = defaultdict(list)
        for cour, pre in prerequisites:
            graph[cour].append(pre)
        
        visited = set()
        def dfs(cour):
            if cour in visited:
                return False
            if not graph[cour]: # 沒有先修課程
                return True

            visited.add(cour)

            for pre in graph[cour]:
                if not dfs(pre):
                    return False
            
            # 走到這裡 代表這個課程前面的路(pre)沒有問題
            # 為了加快速度
            graph[cour] = []
            visited.remove(cour)
            return True

        for cour in range(numCourses):
            if not dfs(cour):
                return False
        return True
```
2023/10/27
```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        record = defaultdict(list)
        for cour, pre in prerequisites:
            record[cour].append(pre)
    
        
        def dfs(cour, visited):
            if not record[cour]:
                return True
            
            if cour in visited:
                return False
            
            visited.add(cour)
            for pre in record[cour]:
                if not dfs(pre, visited):
                    return False
            
            record[cour] = []
            return True
        
        for i in range(numCourses):
            if not dfs(i, set()):
                return False
        return True
```
2023/12/08
```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        graph = collections.defaultdict(list)
        for c, p in prerequisites:
            graph[c].append(p)

        visited = set()

        def dfs(cour):
            visited.add(cour)
            for pre in graph[cour]:
                if pre in visited:
                    return False
                if not dfs(pre):
                    return False
            visited.remove(cour)
            graph[cour] = []
            return True
        
        for n in range(numCourses):
            if not dfs(n):
                return False
        return True
```
2024/02/14
```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        graph = collections.defaultdict(list)
        for cou, pre in prerequisites:
            graph[cou].append(pre)
        
        visited = set()
        def dfs(c):
            if not graph[c]:
                return True 
            if c in visited:
                return False
            
            visited.add(c)
            for pre in graph[c]:
                if not dfs(pre):
                    return False

            visited.remove(c)
            graph[c] = [] # optimization
            return True
        
        for c in range(numCourses):
            if not dfs(c):
                return False
        
        return True
```

2024/09/20
會需要remove，是因為可能會有
```
         1
0   -          -   3
         2
我們是從3走回去，用dfs會先走3-1-0，再來走3-2-0，
如果沒有remove的話，0會被標記為已經走過，會錯誤
```
```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        record = defaultdict(list)
        for cor, pre in prerequisites:
            record[cor].append(pre)
        
        def dfs(n, visited):
            if n in visited: # circle
                return False
            if not record[n]: # None
                return True

            visited.add(n)
            for pre in record[n]:
                if not dfs(pre, visited):
                    return False

            visited.remove(n)
            record[n] = []

            return True
        
        for n in range(numCourses):
            if not dfs(n, set()):
                return False
        return True
```
2024/10/11
拜託記得remove，同一個course的dfs也會有相同的pre
```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        record = defaultdict(list)
        for cour, pre in prerequisites:
            record[cour].append(pre)
        
        for c in range(numCourses):
            visited = set()
            if not self.dfs(c, visited, record):
                return False
        return True

    def dfs(self, c, visited, record):
        if c in visited:
            return False
        if not record[c]:
            return True
        
        visited.add(c)
        for pre in record[c]:
            if not self.dfs(pre, visited, record):
                return False
        
        visited.remove(c)
        record[c] = []
        return True
```


---
### **優化**

如果圖形如下面那張圖所示，主函數的for迴圈從4開始思考，會直接通過，再來從3開始思考，會先visit.add(3)，考慮4，再visit.remove(3)，最後回傳true給主函數，讓他可以繼續往1去思考

時間複雜度O(n)
創建 preMap 字典的時間複雜度為 O(n)，其中 n 是課程數量。
DFS 遍歷所有的課程節點，時間複雜度為 O(n)。
在 DFS 遍歷過程中，每個課程節點最多只會被訪問一次，因此整個算法的時間複雜度為 O(n)。

空間複雜度O(n)
preMap 字典的空間複雜度為 O(n)。
visit 集合的空間複雜度為 O(n)。
DFS 遞迴深度最多為 n，因此整個算法的空間複雜度為 O(n)。

```python
class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        preMap = { i : [] for i in range(numCourses) } # 創建空dict
        for course, prere in prerequisites: # 創建course -> prere
            preMap[course].append(prere)

        visit = set()
        def dfs(course):
            if course in visit:
                return False
            if preMap[course] == []:
                return True

            visit.add(course)
            for pre in preMap[course]:
                if not dfs(pre): 
                    return False
            visit.remove(course)
            preMap[course] = []
            return True

        for course in range(numCourses):
            if not dfs(course):
                return False
            else:
                return True
```
---


**:warning: 錯誤語法** 
:::warning

:::


**:thumbsup:學習** 
:::success

:::

**思路**
![](https://i.imgur.com/LWTepGi.png)


**講解連結**
https://www.youtube.com/watch?v=EgI5nU9etnU
https://blog.csdn.net/fuxuemingzhu/article/details/82951771
Provided by. NeetCode & 负雪明烛

###### tags: `graph` `medium` `leetcode` `值得再想`