最短路之dijkstra

【模板题】单源最短路径一只棠子的dijkstra,启动!

背景介绍

Dijkstra算法是由荷兰计算机科学家Edsger W. Dijkstra在1956年提出的一种用于计算图中单源最短路径的算法。它以贪心策略为基础,通过逐步扩展已知最短路径的顶点集合,找到从起始点到所有其他顶点的最短路径。

Dijkstra算法的基本原理

Dijkstra算法的基本思想是通过逐步选择距离起始点最近的未访问顶点,更新其邻接顶点的最短路径,直到所有顶点都被访问到。具体步骤如下:

  1. 初始化

    • 设定起始顶点的距离为0,其余顶点的距离为正无穷大。
    • 使用一个优先队列(通常用最小堆实现)来存储每个顶点及其当前已知的最短距离。
    • 设定一个已访问顶点的集合,初始为空。
  2. 迭代更新

    • 每次从优先队列中取出距离最小的顶点作为当前顶点,并标记为已访问。
    • 更新当前顶点的所有邻接顶点的最短距离,如果通过当前顶点到某个邻接顶点的路径比已知的路径更短,则更新该邻接顶点的最短距离,并将其加入优先队列。
    • 重复上述步骤,直到优先队列为空。

算法时间复杂度

Dijkstra算法的时间复杂度主要取决于图的表示方式和优先队列的实现方式:

  • 使用邻接矩阵和简单数组实现优先队列时,时间复杂度为O(V^2),其中V是顶点数。
  • 使用邻接表二叉堆实现优先队列时,时间复杂度为O((V + E) log V),其中E是边数。

Dijkstra算法的优缺点

优点
  • 适用于边权非负的图。
  • 算法简单易于实现。
  • 通过使用优先队列,算法在稀疏图中表现优异。
缺点
  • 不适用于存在负权边的图(负权边可能导致算法陷入无限循环)。
  • 在某些情况下,可能不如其他最短路径算法(如Floyd-Warshall算法)高效。

话不多说,上模板题!

【模板题】单源最短路径

         这是一个很经典的堆优化的dijkstra😎

根据原理开始套!

一:初始化

dis = {i: float('inf') for i in graph} 
dis[start] = 0 
queue = [(0, start)] 
  • dis = {i: float('inf') for i in graph}

    初始化距离字典dis,所有节点的初始距离设为正无穷大。
  • dis[start] = 0

    将起点start的距离设为0,即所求点到本身的距离为0。
  • queue = [(0, start)]

    将起点start和当前花费0以元组形式加入优先队列queue

二:迭代更新 

while queue:  # 队列不为空
        cost, node = heappop(queue)  # 从队列中弹出花费最小的节点
        if cost > dis[node]: 
            continue

        for neighbor, edge in graph[node]:  # 遍历当前节点的邻居节点及对应的边权重
            distance = cost + edge
            if distance < dis[neighbor]:
                dis[neighbor] = distance
                heappush(queue, (distance, neighbor))  # 将邻居节点及从起点到达邻居节点的新花费加入队列

    return dis

  • while queue:

    优先队列不为空时,继续循环。
  • cost, node = heappop(queue)

    从优先队列中弹出花费最小的节点及其对应的花费。
  • if cost > dis[node]:

    如果当前节点的花费大于已知最小花费,说明是过期数据,跳过。
  • for neighbor, edge in graph[node]:

    遍历当前节点的所有邻居节点及对应的边权重。
  • if distance < dis[neighbor]:

    如果新的花费小于已知最小花费,则更新。
  • dis[neighbor] = distance

    更新邻居节点的最小花费。
  • heappush(queue, (distance, neighbor))

    将邻居节点及新的花费加入优先队列。
  • return dis

    返回从起点到所有节点的最小花费。

 三:输入输出

n, m , s= map(int, input().split())# 读取输入
graph = {i: [] for i in range(1, n + 1)}  # 创建一个空图,用字典表示,键为节点编号,值为与该节点相连的边及权重
for i in range(m):
    u, v, w = map(int, input().split())
    graph[u].append((v, w))

result = dijkstra(graph, s) #s到每个点的距离

print(*result.values())

graph = {i: [] for i in range(1, n + 1)}

  • 创建图,用字典表示,键为节点编号,值为与该节点相连的边及权重。

graph[u].append((v, w))

  • 将边的信息加入图的邻接表中。

以下是完整代码,嘿咻~

from heapq import heappop, heappush

def dijkstra(graph, start):
    dis = {i: float('inf') for i in graph}  # 初始化距离字典,起点距离为0,其它点距离为无穷大
    dis[start] = 0
    queue = [(0, start)]  # 起点start和当前花费0
    while queue:  # 队列不为空
        cost, node = heappop(queue)  # 从队列中弹出花费最小的节点
        if cost > dis[node]:  # 如果取出的距离比已知的距离大,说明是旧数据,忽略
            continue

        for neighbor, edge in graph[node]:  # 遍历当前节点的邻居节点及对应的边权重
            distance = cost + edge
            if distance < dis[neighbor]:
                dis[neighbor] = distance
                heappush(queue, (distance, neighbor))  # 将邻居节点及从起点到达邻居节点的新花费加入队列

    return dis


n, m , s= map(int, input().split())# 读取输入
graph = {i: [] for i in range(1, n + 1)}  # 创建一个空图,用字典表示,键为节点编号,值为与该节点相连的边及权重
for i in range(m):
    u, v, w = map(int, input().split())
    graph[u].append((v, w))

# 计算从起点1到所有节点的最短路径
result = dijkstra(graph, s)

print(*result.values())

棠子的其他板

对于不同的题目要有不同的模板,不过记得不能把思维被板子限制住嘞!

一:两点最短路

 (是最初的板子,不是很常用)

from heapq import heappop,heappush
def dijkstra(graph,start,end):
    queue=[(0,start)]#起点start和当前花费0
    visited=set()#集合,记录已经访问过的节点 
    while queue:#队列不为空
        cost,node=heappop(queue)#从队列中弹出花费最小的节点
        if node==end:#终点
            return cost
        if node not in visited:
            visited.add(node)#标记当前节点
            for neighbor , edge in graph[node]:#遍历当前节点的邻居节点及对应的边权重。
                if neighbor not in visited:# 如果邻居节点没有被访问过,则将其加入队列中以便后续处理。
                    heappush(queue,(cost+edge,neighbor))#将邻居节点及从起点到达邻居节点的新花费加入队列。
    return -1#如果没有找到从起点到终点的路径
n,m=map(int,input().split())
graph={i:[] for i in range(1,n+1)}#创建一个空图,用字典表示,键为 节点 编号,值为与该节点相连的 边 及 权重 。
for i in range(m):
    x=int(input())
    graph[x].append((x,x))  
result=dijkstra(graph,1,n)
print(result)#输出最短路的花费

二:堆优化(一点到其他所有顶点的最短路径)

from heapq import heappop,heappush

def dijkstra(graph,start,end):
    dis={i:float('inf') for i in graph} # 初始化距离字典,起点距离为0,其它点距离为无穷大
    dis[start]=0
    queue=[(0,start)]#起点start和当前花费0
    while queue:#队列不为空
        cost,node=heappop(queue)#从队列中弹出花费最小的节点
        if cost >dis[node]:# 如果取出的距离比已知的距离大,说明是旧数据,忽略
            continue

        
        for neighbor , edge in graph[node]:#遍历当前节点的邻居节点及对应的边权重。
            distance=cost+edge
            if distance<dis[neighbor]:
                dis[neighbor]=distance
                heappush(queue,(distance,neighbor))#将邻居节点及从起点到达邻居节点的新花费加入队列。
    return distance
n,m=map(int,input().split())
graph={i:[] for i in range(1,n+1)}#创建一个空图,用字典表示,键为 节点 编号,值为与该节点相连的 边 及 权重 。
for i in range(m):
    u,v,w=map(int,input().split())
    graph[u].append((v,w))
result=dijkstra(graph,1,n)
print(result)#输出最短路的花费

三:最短路径(包括经过的节点序列)

import heapq

def dijkstra(graph, start, end):
    dis = {i: float('inf') for i in graph}  # 初始化距离字典,起点距离为0,其它点距离为无穷大
    dis[start] = 0

    prnode = {i: None for i in graph}  # 初始化前驱字典,用于恢复路径

    queue = [(0, start)]  # 起点start和当前花费0
    while queue:  # 队列不为空
        cost, node = heapq.heappop(queue)  # 从队列中弹出花费最小的节点
        if cost > dis[node]:  # 如果取出的距离比已知的距离大,说明是旧数据,忽略
            continue

        for neighbor, edge in graph[node]:  # 遍历当前节点的邻居节点及对应的边权重。
            distance = cost + edge
            if distance < dis[neighbor]:
                dis[neighbor] = distance
                prnode[neighbor] = node
                heapq.heappush(queue, (distance, neighbor))  # 将邻居节点及从起点到达邻居节点的新花费加入队列。

    return dis, prnode

def find_shortest_path(prnode, start, target):
    path = []
    cnode = target
    while cnode is not None:
        path.append(cnode)
        cnode = prnode[cnode]
    return path[::-1]

n, m = map(int, input().split())
graph = {i: [] for i in range(1, n + 1)}  # 创建一个空图,用字典表示,键为节点编号,值为与该节点相连的边及权重。

for i in range(m):
    u, v, w = map(int, input().split())
    graph[u].append((v, w))
    graph[v].append((u, w))  # 因为是无向图,需要添加双向边

distances, predecessors = dijkstra(graph, 1, n)

# 输出结果
if distances[n] == float('inf'):
    print(-1)
else:
    path = find_shortest_path(predecessors, 1, n)
    print(' '.join(map(str, path)))

加训!!!

先简单练练手

来自Codeforces

洛谷P1744

再试试思维突破!

洛谷P2865

Codeforces

  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只棠子

棠子:给脆穷大学生一点支持吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值