【图算法】 Dijkstra算法与Floyd算法

Dijkstra算法与Floyd算法分别用于求单源最短路径与多源最短路径,其中Dijkstra算法要求图中所有边的权值为正,而Floyd算法只要求不存在负权环就行(即可以存在负权的边,但是若有环,环的权值和不能为负)。
本文在给出这两个算法的求解代码的同时,给出各自求解路径的代码。

Dijkstra算法

Dijkstra算法是一个贪心算法,其思想是,设置一个从源点到顶点最短距离已知的点的集合S,然后不断地做贪心选择来扩充这个集合。最初时,S仅包含源点,从剩余的点的集合V-SV是所有顶点的集合)中选择一个距离源点最近的点,把它加入到S中来,然后以这个点更新距离矩阵,不断重复这个过程,直至S包含所有的点。

代码如下

def dijkstra(matrix,sp,query):
    """
    matrix: 各节点距离矩阵,以无穷代表无连接,N*N
    sp    : 源点坐标
    query : 查询路径的点
    """
    N = len(matrix)
    # S集合,一开始为空
    s = [False for i in range(N)] 
    # 各点至源点的距离
    dist = [0 for i in range(N)]
    # 前导节点
    prev = [-1 for i in range(N)]
    for i in range(N):
        dist[i] = matrix[sp][i]
        if dist[i] == float('inf'):
            prev[i] = -1
        else:
            prev[i] = sp
   # 把源点加入集合S中
    prev[sp] = -1
    s[sp] = True
   
    for i in range(N):
        # 从源点出发,找V-S中距离源点最近的点
        u = sp
        temp = float('inf')
        # 每次更新V-S中最近的距离的点
        for j in range(N):
            if not s[j] and dist[j] < temp:
                u = j
                temp = dist[j]
        # 把找到的点加入S集合中
        s[u] = True
        # 依据新找到的点,更新距离
        for j in range(N):
            # 如果点j在V-S中,且与u有连接
            if not s[j] and matrix[u][j] < float('inf'):
               # 从源点出发,经过u到j的距离
                new_dist = dist[u] + matrix[u][j]
                # 如果这个距离比原先的距离小,则更新距离,并更新前导节点
                if new_dist < dist[j]:
                    dist[j] = new_dist
                    prev[j] = u
    # 寻找路径
    # 实际上,路径记录在prev中,假设要找起点至某个点j的路径,只需不断回溯prev[j]就行,直至其等于源点坐标
    route = []
    dst = query
    while dst != sp:
        route.append(dst)
        dst = prev[dst]
    route.append(sp)
    route.reverse()
    # 打印路径
    print('从源点至%d点的路径为:'%query)
    for i in range(len(route)-1):
        print('%d -> '%route[i],end='')
    print(route[-1])
    return dist,prev

Floyd算法

Floyd算法用于求多源最短路径,即各个点之间的最短路径,给定初始距离矩阵,其思想是从i点到j的距离,是否可以通过若干个中间点变得更短,如果可以,则更新距离。具体过程可参考这里
同样地,可以在每次更新距离时记录前导节点,在寻找路径时,需要不断回溯。

代码如下:

def floyd(matrix,query):
    """
    matrix: 初始距离矩阵
    query : 查询从i点到j点的路径
    """
    N = len(matrix)
    # 前导节点矩阵,-1表示i与j之间没有通过中间点连接
    prev = [[-1 for j in range(N)] for i in range(N)]
    
    # k是中间点
    for k in range(N):
        for i in range(N):
            for j in range(N):
                # 如果通过k连接,距离会变得更短,则更新距离和前导节点
                if matrix[i][k] + matrix[k][j] < matrix[i][j]:
                    matrix[i][j] = matrix[i][k] + matrix[k][j]
                    prev[i][j] = k

    # 寻找路径
    # 现在得到的路径中的值是这样的 i -> j 要经过k点,
    # 所以假设要求i -> j的路径,先读出path[i][j],如果这个值为-1,表示直接可以到,不需要中转
    # 如果这个值为k,再读取path[k][j],如果为-1,则路径是i->k->j,同理,若仍为某个k2,则继续往下
    # 直至path中的值是-1
    src,dst = query[0],query[1]
    route = [src]
    while prev[src][dst] != -1:
        src = prev[src][dst]
        route.append(src)
    route.append(dst)
    print('从%d到%d的路径为:'%(query[0],query[1]))
    for i in range(len(route)-1):
        print('%d -> '%route[i],end='')
    print(route[-1])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值