mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 13:12:44 +00:00
chore: clean up content phase organization
This commit is contained in:
1 parent
403a2d7f23
commit
bf0ac34a43
138 files changed
+1436
-368
No files matched your search
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: '207. Course Schedule'
|
||||
description: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai
|
||||
sidebar:
|
||||
label: 'Course Schedule'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
<Badge variant="accent">Depth-First Search</Badge>
|
||||
|
||||
### Example 1:
|
||||
- Input: `numCourses = 2, prerequisites = [[1,0]]`
|
||||
- Output: `true`
|
||||
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`. So it is possible.
|
||||
|
||||
### Example 2:
|
||||
- Input: `numCourses = 2, prerequisites = [[1,0],[0,1]]`
|
||||
- Output: `false`
|
||||
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`, and to take course `0` you should also have finished course `1`. So it is impossible.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= numCourses <= 2000`
|
||||
- `0 <= prerequisites.length <= 5000`
|
||||
- `prerequisites[i].length == 2`
|
||||
- `0 <= ai, bi < numCourses`
|
||||
- All the pairs `prerequisites[i]` are unique.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool:
|
||||
indegree = [0] * numCourses
|
||||
adj = [[] for x in range(numCourses)]
|
||||
for prereq in prerequisites:
|
||||
adj[prereq[1]].append(prereq[0])
|
||||
indegree[prereq[0]] += 1
|
||||
|
||||
queue = []
|
||||
|
||||
for i in range(numCourses):
|
||||
if indegree[i] == 0:
|
||||
queue.append(i)
|
||||
|
||||
visited = 0
|
||||
while queue:
|
||||
node = queue.pop(0)
|
||||
visited += 1
|
||||
for neighbor in adj[node]:
|
||||
indegree[neighbor] -= 1
|
||||
if indegree[neighbor] == 0:
|
||||
queue.append(neighbor)
|
||||
|
||||
return numCourses == visited
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: '210. Course Schedule II'
|
||||
description: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai
|
||||
sidebar:
|
||||
label: 'Course Schedule II'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
<Badge variant="accent">Depth-First Search</Badge>
|
||||
|
||||
### Example 1:
|
||||
- Input: `numCourses = 2, prerequisites = [[1,0]]`
|
||||
- Output: `[0,1]`
|
||||
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`. So the correct course order is `[0,1]`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]`
|
||||
- Output: `[0,2,1,3]`
|
||||
- Explanation: There are a total of `4` courses to take. To take course `3` you should have finished both courses `1` and `2`. Both courses `1` and `2` should be taken after you finished course `0`. So one correct course order is `[0,1,2,3]`. Another correct ordering is `[0,2,1,3]`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `numCourses = 1, prerequisites = []`
|
||||
- Output: `[0]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= numCourses <= 2000`
|
||||
- `0 <= prerequisites.length <= numCourses * (numCourses - 1)`
|
||||
- `prerequisites[i].length == 2`
|
||||
- `0 <= ai, bi < numCourses`
|
||||
- `ai != bi`
|
||||
- All the pairs [ai, bi] are distinct.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findOrder(self, numCourses: int, prerequisites: list[list[int]]) -> list[int]:
|
||||
prereq = {c: [] for c in range(numCourses)}
|
||||
for crs, pre in prerequisites:
|
||||
prereq[crs].append(pre)
|
||||
|
||||
# course has 3 possible states:
|
||||
# visited -> crs has been added to output
|
||||
# visiting -> crs not added to output, but added to cycle
|
||||
# unvisited -> crs not added to output nor cycle
|
||||
output = []
|
||||
visit, cycle = set(), set()
|
||||
|
||||
def dfs(crs):
|
||||
if crs in cycle:
|
||||
return False
|
||||
if crs in visit:
|
||||
return True
|
||||
|
||||
cycle.add(crs)
|
||||
for pre in prereq[crs]:
|
||||
if dfs(pre) == False:
|
||||
return False
|
||||
|
||||
cycle.remove(crs)
|
||||
visit.add(crs)
|
||||
output.append(crs)
|
||||
return True
|
||||
|
||||
for c in range(numCourses):
|
||||
if dfs(c) == False:
|
||||
return []
|
||||
|
||||
return output
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Topological Sort
|
||||
description: 'Topological sort or topological ordering of a directed graph is an ordering of nodes such that every node appears in the ordering before all the nodes it points to. '
|
||||
hidden: true
|
||||
---
|
||||
|
||||
## Directed Graph
|
||||
|
||||
Before we get started on topological order, let's talk about directed graphs first. A graph is directed when its edges have directions (these edges are also called arcs). Suppose that v, w are vertices in a directed graph. Let's get familiar with a few concepts.
|
||||
|
||||
- **in-edge** - an edge pointing to or coming into the node
|
||||
- **out-edge** - an edge pointing away or going out from the node (we will refer those nodes on the other end of the out-edges of v to v's neighbors)
|
||||
- **in-degree** - the number of in-edges coming into the node
|
||||
- **out-degree** - the number of out-edges going out from the node
|
||||
- **dependency** - "v depends on w" / "v is dependent on w" means that there is a directed path from w to v where v, w are vertices in a directed graph. With these new terms under our belt, it will make more sense when we talk about directed graphs.
|
||||
|
||||
## What is a topological order?
|
||||
|
||||
Topological sort is not unique. For example, for a graph like this:
|
||||
|
||||

|
||||
|
||||
Both `[4, 5, 2]` and `[5, 4, 2]` are valid topological orders.
|
||||
|
||||

|
||||
|
||||
Task 3 is completely independent of task 2 and 4, and it can be anywhere in the order as long as it is before task 1 which depends on it. All of the following ordering are valid topological orderings.
|
||||
|
||||
`[4, 2, 3, 1], [4, 3, 2, 1], [3, 4, 2, 1]`
|
||||
|
||||
|
||||
:::warning[Graphs with Cycles Do Not Have Topological Ordering]
|
||||
It should be obvious that if a graph with a cycle does not have a topological ordering. In the above example, 2 has to come before 5 which has come before 4 which has to come before 2 which has to come before 5... which is impossible.
|
||||
:::
|
||||
|
||||
## Kahn's Algorithm
|
||||
|
||||
To obtain a topological order, we can use Kahn's algorithm which is very similar to Breadth First Search.
|
||||
|
||||
In order to understand why we must use this algorithm we first look at the problem it is trying to solve.
|
||||
|
||||
Given a directed graph does there exist a way to remove the nodes such that each time we remove a node we guarantee that no other nodes point to that particular node?
|
||||
|
||||
For this algorithm, we systematically remove one node at a time, each time removing a node such that no other nodes point to that node (in-degree is 0). If no such node exists, then there must be a cycle, and there is no way to order the nodes such that "every node appears in the ordering before all the nodes it points to" (no solution). After removing the current node, for each neighboring node the current node points to, we check whether any nodes point to this node. If there isn't any, we push this node into the queue. Notice it is important to keep track of the number of nodes pointing to a node (in-degree) in the question as we only push the node into the queue once all nodes the current node depended on have been removed.
|
||||
|
||||
Here is a graphic to demonstrate the idea.
|
||||
|
||||
<Carousel>
|
||||
<CarouselImage src='../../../public/topo_kahn.001.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.002.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.003.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.004.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.005.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.006.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.007.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.009.png' />
|
||||
<CarouselImage src='../../../public/topo_kahn.010.png' />
|
||||
</Carousel>
|
||||
|
||||
## Example Code
|
||||
|
||||
:::tip
|
||||
Notice the topological sort algorithm is very similar to BFS. The main difference is that we only push nodes with 0 in-degree into the queue in topological sort whereas in BFS we push all the neighboring nodes into the queue.
|
||||
:::
|
||||
|
||||
```py
|
||||
from collections import deque
|
||||
|
||||
def find_indegree(graph):
|
||||
indegree = { node: 0 for node in graph } # dict
|
||||
for node in graph:
|
||||
for neighbor in graph[node]:
|
||||
indegree[neighbor] += 1
|
||||
return indegree
|
||||
|
||||
|
||||
def topo_sort(graph):
|
||||
res = []
|
||||
q = deque()
|
||||
indegree = find_indegree(graph)
|
||||
for node in indegree:
|
||||
if indegree[node] == 0:
|
||||
q.append(node)
|
||||
while len(q) > 0:
|
||||
node = q.popleft()
|
||||
res.append(node)
|
||||
for neighbor in graph[node]:
|
||||
indegree[neighbor] -= 1
|
||||
if indegree[neighbor] == 0:
|
||||
q.append(neighbor)
|
||||
return res if len(graph) == len(res) else None
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
import { defineMeta } from "blume";
|
||||
|
||||
export default defineMeta({ title: "Topological Sort", order: 18, icon: "workflow", display: "group" });
|
||||
Reference in new issue
Block a user