最短路径算法

https://blog.csdn.net/qq_35644234/article/details/61614581
1 Dijkstra算法介绍
Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,
然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比dis中的短,如果是,那么就替换这些顶点在dis中的值,新加入的顶点标记为已访问。
然后,又从dis中未标记中找出最小值,作为下次新加入的点,重复上述动作,直到T中包含了图的所有顶点。
python代码

# dijkstra算法实现,有向图和路由的源点作为函数的输入,最短路径最为输出
def dijkstra(graph,src):
    # 判断图是否为空,如果为空直接退出
    if graph is None:
        return None
    nodes = [i for i in range(len(graph))]  # 获取图中所有节点
    visited=[]  # 表示已经路由到最短路径的节点集合
    if src in nodes:
        visited.append(src)
        nodes.remove(src)
    else:
        return None
    distance={src:0}  # 记录源节点到各个节点的距离
    for i in nodes:
        distance[i]=graph[src][i]  # 初始化
    # print(distance)
    path={src:{src:[]}}  # 记录源节点到每个节点的路径
    k=pre=src
    while nodes:
        mid_distance=float('inf')
        for v in visited:
            for d in nodes:
                new_distance = graph[src][v]+graph[v][d]
                if new_distance < mid_distance:
                    mid_distance=new_distance
                    graph[src][d]=new_distance  # 进行距离更新
                    k=d
                    pre=v
        distance[k]=mid_distance  # 最短路径
        path[src][k]=[i for i in path[src][pre]]
        path[src][k].append(k)
        # 更新两个节点集合
        visited.append(k)
        nodes.remove(k)
        print(visited,nodes)  # 输出节点的添加过程
    return distance,path
if __name__ == '__main__':
    graph_list = [ [0, 2, 1, 4, 5, 1],
            [1, 0, 4, 2, 3, 4],
            [2, 1, 0, 1, 2, 4],
            [3, 5, 2, 0, 3, 3],
            [2, 4, 3, 4, 0, 1],
            [3, 4, 7, 3, 1, 0]]

    distance,path= dijkstra(graph_list, 0)  # 查找从源点0开始带其他节点的最短路径
    print(distance,path)


2 Floyd算法
通过Floyd计算图G=(V,E)中各个顶点的最短路径时,需要引入两个矩阵,矩阵S中的元素a[i][j]表示顶点i(第i个顶点)到顶点j(第j个顶点)的距离。矩阵P中的元素b[i][j],表示顶点i到顶点j经过了b[i][j]记录的值所表示的顶点。

假设图G中顶点个数为N,则需要对矩阵D和矩阵P进行N次更新。初始时,矩阵D中顶点a[i][j]的距离为顶点i到顶点j的权值;如果i和j不相邻,则a[i][j]=∞,矩阵P的值为顶点b[i][j]的j的值。 接下来开始,对矩阵D进行N次更新。第1次更新时,如果”a[i][j]的距离” > “a[i][0]+a[0][j]”(a[i][0]+a[0][j]表示”i与j之间经过第1个顶点的距离”),则更新a[i][j]为”a[i][0]+a[0][j]”,更新b[i][j]=b[i][0]。 同理,第k次更新时,如果”a[i][j]的距离” > “a[i][k-1]+a[k-1][j]”,则更新a[i][j]为”a[i][k-1]+a[k-1][j]”,b[i][j]=b[i][k-1]。更新N次之后,操作完成!

import sys
def floyd(l, n):
    '''
    l: l[i][j] = distace of i and j if <i, j> in E else sys.maxint
    k: sum of point
    '''
    d = l[:]
    
    route = [([''] * n) for i in range(n)]
    for i in range(n):
        for j in range(n):
            if d[i][j]:
                route[i][j] = str(i + 1) + " " + str(j + 1)
    
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if d[i][j] > d[i][k] + d[k][j]:
                    d[i][j] = d[i][k] + d[k][j]
                    route[i][j] = route[i][k] + " " + route[k][j][2:]
    return d, route
if __name__ == "__main__":
    n = 3
    l = [[0, 2, 9], [8, 0, 6], [1, sys.maxint, 0]]
    d, route = floyd(l, n)
    
    for i in d:
        for j in i:
            print j,
        print ""
    
    for i in route:
        for j in i:
            print "[" + j + "],",

3 SPFA算法
SPFA算法是求解单源最短路径问题的一种算法,由理查德·贝尔曼(Richard Bellman) 和 莱斯特·福特 创立的。有时候这种算法也被称为 Moore-Bellman-Ford 算法,因为 Edward F. Moore 也为这个算法的发展做出了贡献。它的原理是对图进行V-1次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达 O(VE)。但算法可以进行若干种优化,提高了效率。

算法的思路:
我们用数组dis记录每个结点的最短路径估计值,用邻接表或邻接矩阵来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止

我们要知道带有负环的图是没有最短路径的,所以我们在执行算法的时候,要判断图是否带有负环,方法有两种:

开始算法前,调用拓扑排序进行判断(一般不采用,浪费时间)
如果某个点进入队列的次数超过N次则存在负环(N为图的顶点数)

#coding=utf-8

global n, m, k, edge, head, dis, stack, vis, nMax, mMax, inf

nMax = 100

mMax = 10000

inf = 1e+10

class e(object):

    pass

n = 0

k = 0

m = 0

eg = e()

edge = []

head = [0]

dis = [0]

stack = [0]

vis = [0]

def addedge(a, b, c):

    global k, edge, head

    ed = e()

    ed.u = a #you can delect it

    ed.v = b

    ed.w = c

    ed.next = head[a]

    edge.append(ed)

    head[a]=k

    k+=1

    pass

def spfa():

    global n, m, k, edge, head, dis, stack, vis,inf

    i = top = 0

    for i in range(0 , n):

        dis[i] = inf

        vis[i] = 0

    dis[0] = 0

    vis[0] = 1

    top+=1

    stack[top] = 0

    while(top!=0):

        u = stack[top]

        top-=1

        i = head[u]

        while(i!=0):

            v = edge[i].v

            if dis[v] > dis[u]+edge[i].w:

                dis[v] = dis[u]+edge[i].w

                if(vis[v]==0):

                    vis[v] = 1

                    top+=1

                    stack[top] = v

            i = edge[i].next

        vis[u] = 0

    pass

   

if __name__ == '__main__':

    u = v = l = i = 0

    for i in range(0,nMax):

        head.append(0);

        dis.append(0)

        vis.append(0)

        stack.append(0)

    while(1):

        na = input()

        n = int(na)

        ma = input()

        m = int(ma)

        edge=[0]

        k = 1

        for i in range(0,n):

            head[i] = 0

        for i in range(0,m):

            ua = input()       

            va = input()

            la = input()

            u = int(ua)

            v = int(va)

            l = int(la)

            addedge(u,v,l)

        spfa()  

        for i in range(1,n):

            print(dis[i])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值