从最短路径到最小生成树,理解 Dijkstra、Prim、Kruskal 和阿克曼函数

📌 前言

今天学习了贪心算法的Dijkstra和最小生成树的Prim、Kruskal,数据结构图、矩阵、最小堆,以及阿克曼函数等:

  • Dijkstra 算法(单源最短路径)
  • Prim 和 Kruskal 算法(最小生成树)
  • 最小堆(优化路径搜索)
  • 阿克曼函数与反阿克曼函数(超快增长的数学函数)
  • 图的存储方式(邻接矩阵 vs 邻接表)
  • 三对角矩阵(数值计算优化)

1️⃣ Dijkstra 算法:找最短路径的"导航系统"

❓ 问题:如何从A点走到B点,路程最短?

想象你开车在城市中导航,Google Maps 需要计算 最短路径 。Dijkstra 算法就像一个聪明的导航助手,帮你找到 从起点到终点的最短路径

🎯 Dijkstra 的核心思路

  1. 先假设所有路径都很远,起点到自身的距离为 0,其他点距离是无穷大。
  2. 每次选择当前最短的路径,进行扩展(贪心策略)。
  3. 用最小堆加速选择最优路径(不然太慢)。
  4. 重复这个过程,直到到达终点

📝 代码框架

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 算法(适用于稠密图)

💡 思路

  1. 从某个点开始,每次选最短的边,扩展新点(像建长城)。
  2. 不断扩展,直到所有点都被连通

📝 代码框架(最小堆优化)

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 算法(适用于稀疏图)

💡 思路

  1. 把所有的边按照长度排序(像修桥)。
  2. 从最短的开始连接,只要不会成环,就加入(用"并查集"管理)。
  3. 直到所有点都连通

📝 代码框架(并查集优化)

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 负责连线,阿克曼函数是数学怪兽!🔥

相关推荐
小王努力学编程2 分钟前
元音辅音字符串计数leetcode3305,3306
开发语言·c++·学习·算法·leetcode
Coder Zhang8 分钟前
并查集,find函数的压缩路径,Union函数的小树合并大树
数据结构·算法
不忘不弃16 分钟前
矩阵的转置
线性代数·算法·矩阵
A_SHOWY30 分钟前
HOT100系列——(普通数组+矩阵)
算法·leetcode
王网aaa2 小时前
堆结构和堆排序
java·算法·排序算法
m0_675988232 小时前
Leetcode3110:字符串的分数
算法·leetcode·字符串·python3
进击的jerk2 小时前
力扣 11.盛水最多的容器(双指针)
c++·算法·leetcode
竹下为生2 小时前
LeetCode --- 440周赛
算法·leetcode·职场和发展
最好的药物是乌梅3 小时前
【蓝桥杯速成】| 3.数据结构
数据结构·算法·蓝桥杯
*.✧屠苏隐遥(ノ◕ヮ◕)ノ*.✧3 小时前
C语言_数据结构总结10:二叉树的递归/非递归遍历
c语言·数据结构·b树·算法·链表·visualstudio·visual studio