Skip to content

Commit 379f3c4

Browse files
committed
improved doc; improved code of union-find
1 parent 3192964 commit 379f3c4

File tree

7 files changed

+148
-102
lines changed

7 files changed

+148
-102
lines changed

README.md

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# 算法模板
22

3-
![来刷题了](https://img.fuiboom.com/img/title.png)
4-
53
算法模板,最科学的刷题方式,最快速的刷题路径,一个月从入门到 offer,你值得拥有 🐶~
64

75
算法模板顾名思义就是刷题的套路模板,掌握了刷题模板之后,刷题也变得好玩起来了~

basic_algorithm/graph/README.md

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
# 图相关算法
22

3-
Ongoing...
3+
### [深度优先搜索和广度优先搜索](./bfs_dfs.md)
44

5-
[深度优先搜索和广度优先搜索](./bfs_dfs.md)
5+
### [拓扑排序](./topological_sorting.md)
66

7-
[拓扑排序](./topological_sorting.md)
7+
### [最小生成树](./mst.md)
88

9-
[最小生成树](./mst.md)
9+
### [最短路径](./shortest_path.md)
1010

11-
[最短路径](./shortest_path.md)
12-
13-
[图的表示](./graph_representation.md)
11+
### [图的表示](./graph_representation.md)
1412

basic_algorithm/graph/bfs_dfs.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class Solution:
180180
for dr, dc in neighors:
181181
nr, nc = r + dr, c + dc
182182
if 0<= nr < M and 0 <= nc < N:
183-
if A[nr][nc] == 0: # meet and edge
183+
if A[nr][nc] == 0:
184184
A[nr][nc] = -2
185185
bfs.append((nr, nc))
186186
elif A[nr][nc] == 1:

basic_algorithm/graph/mst.md

+16-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
> 地图上有 m 条无向边,每条边 (x, y, w) 表示位置 m 到位置 y 的权值为 w。从位置 0 到 位置 n 可能有多条路径。我们定义一条路径的危险值为这条路径中所有的边的最大权值。请问从位置 0 到 位置 n 所有路径中最小的危险值为多少?
66
7-
最小危险值为最小生成树中 0 到 n 路径上的最大边权。
7+
最小危险值为最小生成树中 0 到 n 路径上的最大边权。以此题为例给出最小生成树的两种经典算法。
8+
9+
- 算法 1: [Kruskal's algorithm]([https://en.wikipedia.org/wiki/Kruskal%27s_algorithm](https://en.wikipedia.org/wiki/Kruskal's_algorithm)),使用[并查集](../../data_structure/union_find.md)实现。
810

911
```Python
1012
# Kruskal's algorithm
@@ -13,6 +15,7 @@ class Solution:
1315

1416
# Kruskal's algorithm with union-find
1517
parent = list(range(N + 1))
18+
rank = [1] * (N + 1)
1619

1720
def find(x):
1821
if parent[parent[x]] != parent[x]:
@@ -21,11 +24,18 @@ class Solution:
2124

2225
def union(x, y):
2326
px, py = find(x), find(y)
24-
if px != py:
27+
if px == py:
28+
return False
29+
30+
if rank[px] > rank[py]:
31+
parent[py] = px
32+
elif rank[px] < rank[py]:
2533
parent[px] = py
26-
return True
2734
else:
28-
return False
35+
parent[px] = py
36+
rank[py] += 1
37+
38+
return True
2939

3040
edges = sorted(zip(W, X, Y))
3141

@@ -34,6 +44,8 @@ class Solution:
3444
return w
3545
```
3646

47+
- 算法 2: [Prim's algorithm]([https://en.wikipedia.org/wiki/Prim%27s_algorithm](https://en.wikipedia.org/wiki/Prim's_algorithm)),使用[优先级队列 (堆)](../../data_structure/heap.md)实现
48+
3749
```Python
3850
# Prim's algorithm
3951
class Solution:

basic_algorithm/graph/shortest_path.md

+74-72
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,84 @@ class Solution:
105105
for dr, dc in neighors:
106106
nr, nc = r + dr, c + dc
107107
if 0<= nr < M and 0 <= nc < N:
108-
if A[nr][nc] == 0: # meet and edge
108+
if A[nr][nc] == 0:
109109
A[nr][nc] = -2
110110
bfs.append((nr, nc))
111111
elif A[nr][nc] == 1:
112112
return flip
113113
flip += 1
114114
```
115115

116+
## Dijkstra's Algorithm
117+
118+
用于求解单源最短路径问题。思想是 greedy 构造 shortest path tree (SPT),每次将当前距离源点最短的不在 SPT 中的结点加入SPT,与构造最小生成树 (MST) 的 Prim's algorithm 非常相似。可以用 priority queue (heap) 实现。
119+
120+
### [network-delay-time](https://leetcode-cn.com/problems/network-delay-time/)
121+
122+
标准的单源最短路径问题,使用朴素的的 Dijikstra 算法即可,可以当成模板使用。
123+
124+
```Python
125+
class Solution:
126+
def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
127+
128+
# construct graph
129+
graph_neighbor = collections.defaultdict(list)
130+
for s, e, t in times:
131+
graph_neighbor[s].append((e, t))
132+
133+
# Dijkstra
134+
SPT = {}
135+
min_heap = [(0, K)]
136+
137+
while min_heap:
138+
delay, node = heapq.heappop(min_heap)
139+
if node not in SPT:
140+
SPT[node] = delay
141+
for n, d in graph_neighbor[node]:
142+
if n not in SPT:
143+
heapq.heappush(min_heap, (d + delay, n))
144+
145+
return max(SPT.values()) if len(SPT) == N else -1
146+
```
147+
148+
### [cheapest-flights-within-k-stops](https://leetcode-cn.com/problems/cheapest-flights-within-k-stops/)
149+
150+
在标准的单源最短路径问题上限制了路径的边数,因此需要同时维护当前 SPT 内每个结点最短路径的边数,当遇到边数更小的路径 (边权和可以更大) 时结点需要重新入堆,以更新后继在边数上限内没达到的结点。
151+
152+
```Python
153+
class Solution:
154+
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, K: int) -> int:
155+
156+
# construct graph
157+
graph_neighbor = collections.defaultdict(list)
158+
for s, e, p in flights:
159+
graph_neighbor[s].append((e, p))
160+
161+
# modified Dijkstra
162+
prices, steps = {}, {}
163+
min_heap = [(0, 0, src)]
164+
165+
while len(min_heap) > 0:
166+
price, step, node = heapq.heappop(min_heap)
167+
168+
if node == dst: # early return
169+
return price
170+
171+
if node not in prices:
172+
prices[node] = price
173+
174+
steps[node] = step
175+
if step <= K:
176+
step += 1
177+
for n, p in graph_neighbor[node]:
178+
if n not in prices or step < steps[n]:
179+
heapq.heappush(min_heap, (p + price, step, n))
180+
181+
return -1
182+
```
183+
184+
## 补充
185+
116186
## Bidrectional BFS
117187

118188
当求点对点的最短路径时,BFS遍历结点数目随路径长度呈指数增长,为缩小遍历结点数目可以考虑从起点 BFS 的同时从终点也做 BFS,当路径相遇时得到最短路径。
@@ -182,81 +252,13 @@ class Solution:
182252
return 0
183253
```
184254

185-
## Dijkstra's Algorithm
186-
187-
用于求解单源最短路径问题。思想是 greedy 构造 shortest path tree (SPT),每次将当前距离源点最短的不在 SPT 中的结点加入SPT,与构造最小生成树 (MST) 的 Prim's algorithm 非常相似。可以用 priority queue (heap) 实现。
188-
189-
### [network-delay-time](https://leetcode-cn.com/problems/network-delay-time/)
190-
191-
标准的单源最短路径问题,使用朴素的的 Dijikstra 算法即可,可以当成模板使用。
192-
193-
```Python
194-
class Solution:
195-
def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
196-
197-
# construct graph
198-
graph_neighbor = collections.defaultdict(list)
199-
for s, e, t in times:
200-
graph_neighbor[s].append((e, t))
201-
202-
# Dijkstra
203-
SPT = {}
204-
min_heap = [(0, K)]
205-
206-
while min_heap:
207-
delay, node = heapq.heappop(min_heap)
208-
if node not in SPT:
209-
SPT[node] = delay
210-
for n, d in graph_neighbor[node]:
211-
if n not in SPT:
212-
heapq.heappush(min_heap, (d + delay, n))
213-
214-
return max(SPT.values()) if len(SPT) == N else -1
215-
```
216-
217-
### [cheapest-flights-within-k-stops](https://leetcode-cn.com/problems/cheapest-flights-within-k-stops/)
218-
219-
在标准的单源最短路径问题上限制了路径的边数,因此需要同时维护当前 SPT 内每个结点最短路径的边数,当遇到边数更小的路径 (边权和可以更大) 时结点需要重新入堆,以更新后继在边数上限内没达到的结点。
220-
221-
```Python
222-
class Solution:
223-
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, K: int) -> int:
224-
225-
# construct graph
226-
graph_neighbor = collections.defaultdict(list)
227-
for s, e, p in flights:
228-
graph_neighbor[s].append((e, p))
229-
230-
# modified Dijkstra
231-
prices, steps = {}, {}
232-
min_heap = [(0, 0, src)]
233-
234-
while len(min_heap) > 0:
235-
price, step, node = heapq.heappop(min_heap)
236-
237-
if node == dst: # early return
238-
return price
239-
240-
if node not in prices:
241-
prices[node] = price
242-
243-
steps[node] = step
244-
if step <= K:
245-
step += 1
246-
for n, p in graph_neighbor[node]:
247-
if n not in prices or step < steps[n]:
248-
heapq.heappush(min_heap, (p + price, step, n))
249-
250-
return -1
251-
```
252-
253-
## 补充:A* Algorithm
255+
## A* Algorithm
254256

255257
当需要求解有目标的最短路径问题时,BFS 或 Dijkstra's algorithm 可能会搜索过多冗余的其他目标从而降低搜索效率,此时可以考虑使用 A* algorithm。原理不展开,有兴趣可以自行搜索。实现上和 Dijkstra’s algorithm 非常相似,只是优先级需要加上一个到目标点距离的估值,这个估值严格小于等于真正的最短距离时保证得到最优解。当 A* algorithm 中的距离估值为 0 时 退化为 BFS 或 Dijkstra’s algorithm。
256258

257259
### [sliding-puzzle](https://leetcode-cn.com/problems/sliding-puzzle)
258260

259-
思路 1:BFS。为了方便对比 A* 算法写成了与其相似的形式。
261+
- 方法 1:BFS。为了方便对比 A* 算法写成了与其相似的形式。
260262

261263
```Python
262264
class Solution:
@@ -300,7 +302,7 @@ class Solution:
300302
return -1
301303
```
302304

303-
思路 2:A* algorithm
305+
- 方法 2:A* algorithm
304306

305307
```Python
306308
class Solution:

basic_algorithm/graph/topological_sorting.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66

77
> 给定课程的先修关系,求一个可行的修课顺序
88
9-
非常经典的拓扑排序应用题目。下面给出 3 种实现方法,可以当做模板使用。
9+
非常经典的拓扑排序应用。下面给出 3 种实现方法,可以当做模板使用。
10+
11+
- 方法 1:DFS 的递归实现
1012

1113
```Python
12-
# 方法 1:DFS 的递归实现
1314
NOT_VISITED = 0
1415
DISCOVERING = 1
1516
VISITED = 2
@@ -42,8 +43,9 @@ class Solution:
4243
return tsort_rev[::-1]
4344
```
4445

46+
- 方法 2:DFS 的迭代实现
47+
4548
```Python
46-
# 方法 2:DFS 的迭代实现
4749
NOT_VISITED = 0
4850
DISCOVERING = 1
4951
VISITED = 2
@@ -81,8 +83,9 @@ class Solution:
8183
return tsort_rev[::-1]
8284
```
8385

86+
- 方法 3:[Kahn's algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm)
87+
8488
```Python
85-
# 方法 3:Kahn's algorithm: https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
8689
class Solution:
8790
def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
8891

0 commit comments

Comments
 (0)