📌 前言
今天学习了贪心算法的Dijkstra和最小生成树的Prim、Kruskal,数据结构图、矩阵、最小堆,以及阿克曼函数等:
- Dijkstra 算法(单源最短路径)
- Prim 和 Kruskal 算法(最小生成树)
- 最小堆(优化路径搜索)
- 阿克曼函数与反阿克曼函数(超快增长的数学函数)
- 图的存储方式(邻接矩阵 vs 邻接表)
- 三对角矩阵(数值计算优化)
1️⃣ Dijkstra 算法:找最短路径的"导航系统"
❓ 问题:如何从A点走到B点,路程最短?
想象你开车在城市中导航,Google Maps 需要计算 最短路径 。Dijkstra 算法就像一个聪明的导航助手,帮你找到 从起点到终点的最短路径。
🎯 Dijkstra 的核心思路
- 先假设所有路径都很远,起点到自身的距离为 0,其他点距离是无穷大。
- 每次选择当前最短的路径,进行扩展(贪心策略)。
- 用最小堆加速选择最优路径(不然太慢)。
- 重复这个过程,直到到达终点。
📝 代码框架
python
import heapq # Python 自带的最小堆
def dijkstra(graph, start):
dist = {node: float('inf') for node in graph}
dist[start] = 0
pq = [(0, start)] # (当前距离, 当前节点)
while pq:
d, node = heapq.heappop(pq) # 取出当前最短路径的点
for neighbor, weight in graph[node]:
new_dist = d + weight
if new_dist < dist[neighbor]:
dist[neighbor] = new_dist
heapq.heappush(pq, (new_dist, neighbor))
return dist
💡 Dijkstra 适用于 :最短路径问题(导航、网络路由、AI 路径规划)。
2️⃣ Prim 和 Kruskal:如何用最少的电线连通整个城市?
❓ 问题:假设你是城市规划师,要用最少的电缆连接所有的建筑物,怎么办?
这就是 最小生成树(MST) 的问题,解决方案有两种:
- Prim 算法(每次扩展最短的边,像"长城"一样慢慢扩展)
- Kruskal 算法(按照边长排序,像"修桥"一样连接两座城市)
🚀 Prim 算法(适用于稠密图)
💡 思路:
- 从某个点开始,每次选最短的边,扩展新点(像建长城)。
- 不断扩展,直到所有点都被连通。
📝 代码框架(最小堆优化)
python
import heapq
def prim(graph, start):
mst_cost = 0
visited = set()
pq = [(0, start)] # (权重, 节点)
while pq:
weight, node = heapq.heappop(pq)
if node in visited:
continue
visited.add(node)
mst_cost += weight
for neighbor, edge_weight in graph[node]:
if neighbor not in visited:
heapq.heappush(pq, (edge_weight, neighbor))
return mst_cost
✅ Prim 适合 边多的稠密图(Dense Graph) ,因为它每次只选最短的边,不需要排序所有边。
🚀 Kruskal 算法(适用于稀疏图)
💡 思路:
- 把所有的边按照长度排序(像修桥)。
- 从最短的开始连接,只要不会成环,就加入(用"并查集"管理)。
- 直到所有点都连通。
📝 代码框架(并查集优化)
python
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # 路径压缩
return self.parent[x]
def union(self, x, y):
rootX, rootY = self.find(x), self.find(y)
if rootX != rootY:
self.parent[rootY] = rootX
def kruskal(edges, n):
uf = UnionFind(n)
mst_cost = 0
edges.sort(key=lambda x: x[2]) # 按边长排序
for u, v, weight in edges:
if uf.find(u) != uf.find(v): # 确保不会成环
uf.union(u, v)
mst_cost += weight
return mst_cost
✅ Kruskal 适合 边少的稀疏图(Sparse Graph) ,因为它 先排序,再连边,不需要检查所有点。
3️⃣ 阿克曼函数:增长速度比指数还恐怖!
阿克曼函数是一种增长超级快的递归函数:
python
def ackermann(m, n):
if m == 0:
return n + 1
if n == 0:
return ackermann(m - 1, 1)
return ackermann(m - 1, ackermann(m, n - 1))
如果你输入:
python
print(ackermann(3, 4)) # 结果是125
但 ackermann(4, 2)
就能让你的电脑死机!
💡 反阿克曼函数(Inverse Ackermann Function)在 并查集优化 中至关重要,它让并查集的查询几乎变成 O(1) 级别 的操作!
4️⃣ 图的存储方式:邻接矩阵 vs 邻接表
💡 图可以用两种方式存储:
存储方式 | 适用场景 | 优势 | 缺点 |
---|---|---|---|
邻接矩阵 | 稠密图 | O(1) 查询边 | O(V²) 空间浪费 |
邻接表 | 稀疏图 | O(V+E) 存储更省 | O(V) 查找某条边 |
结论:
- 如果边很多(稠密图),用邻接矩阵。
- 如果边少(稀疏图),用邻接表。
🎯 总结
✅ Dijkstra :最短路径问题,适用于导航、AI、网络优化。
✅ Prim :最小生成树(MST),适合 边多的稠密图 。
✅ Kruskal :最小生成树(MST),适合 边少的稀疏图 。
✅ 阿克曼函数 :增长极快,反阿克曼函数优化 并查集 。
✅ 图的存储方式:邻接矩阵(适合边多),邻接表(适合边少)。
📌 一句话总结:Dijkstra 负责找路,Prim & Kruskal 负责连线,阿克曼函数是数学怪兽!🔥