单源最短路问题,是固定一个起点,求他到其他所有点的最短距离。终点也固定的问题叫做两点之间的最短路问题。但是两者复杂度一样,因此都当成单源最短路问题求解。
ps:以下问题的默认输入都是边的三元组集和(from,to,cost)

1.Bellman-Ford算法

记从起点s出发到顶点i的最短路径为d[i]. 则下述等式成立。


d[i] = min{d[j]+(从j到i边的权值)|e=(j,i)∈E}

如果给定的图是一个DAG(有向无环图),就可以按拓扑排序给顶点编号,然后利用这条递推关系求出d[i]。但是如果图中存在环,就没办法依赖这样的顺序进行计算了。在这种情况下,设置初始值d[s]=0,d[i] = INF 即可。 只要图中不存在负圈,这样的更新操作就是有限的。结束之后的d就是到各个点的最短距离。

代码:
class Solution(object):
    def Bellman_Ford(self, node_num,edges,start_node):
        """
        :type edges: List[List[int]]
        :rtype: List
        """
        V,E = node_num,len(edges)
        d = [float("inf") for _ in range(V)]
        d[start_node] = 0

        while True:
            updata = False
            for i in range(E):
                from_node,to_node,cost = edges[i]
                if d[from_node]!= float('inf') and d[to_node]>d[from_node]+cost:
                    d[to_node] = d[from_node]+cost
                    updata = True

            if not updata:break

        return d
时间复杂度:O(V*E)

2.Dijkstra算法

让我们考虑下没有负边的情况。在Bellman-Ford算法中,如果d[i]还不是最短距离的话,即使进行d[j]=d[i]+(从i到j的边的权值)的更新,d[j]也不会变为最短距离。而且就算d[i]没有变化,每次循环也要检查一遍从i出发的所有边。这显然是很浪费时间的。因此可以对算法做如下改进:

1. 找到最短路径已经确定的顶点,从它出发更新相邻顶点的最短距离。
2. 从此不需要在关注1中的”最短距离已经确定的顶点“
class Solution(object):
    def Dijstra(self,edges, node_num,start_node):
        """
        :type edges: List[List[int]] 边的集和
        :rtype: List
        """
        d = [float('inf') for _ in range(node_num)]
        d[start_node] = 0
        used = [False for _ in range(node_num)]

        # 建立邻接表
        adj_map = [[] for _ in range(node_num)]
        for edge in edges:
            from_node,to_node,cost = edge
            adj_map[from_node].append((to_node,cost))

        while True:
            # 从未使用过的顶点中选择一个距离最小的顶点
            v = -1
            for i in range(node_num):
                if not used[i] and(v==-1 or d[i]<d[v]) :v = i

            if v==-1:break
            used[v] = True

            # 从距离最小的顶点更新周围的d
            for (to_node,cost) in adj_map[v]:
                d[to_node] = min(d[to_node],d[v]+cost)

        return d
使用堆进行优化

在上面的算法中,每次找最小值的时候,都要遍历整个节点列表,时间复杂度为O(V),但是如果我们使用堆来维护这个节点列表,那么就可以在log(V)的时间内去计算得到。所以我们使用优先队列去实现。

思路:在每次更新最小值的时候,往堆里插入当前最短距离和顶点的二元组。当取出的最小值不是最短距离的时候,跳过该值。

代码

from queue import PriorityQueue
class Solution(object):
    def Dijstra(self,edges, node_num,start_node):
        """
        :type edges: List[List[int]]
        :rtype: list
        """
        d = [float('inf') for _ in range(node_num)]
        d[start_node] = 0
        queue = PriorityQueue()
        queue.put((0,start_node))

        # 建立邻接表
        adj_map = [[] for _ in range(node_num)]
        for edge in edges:
            from_node,to_node,cost = edge
            adj_map[from_node].append((to_node,cost))

        while not queue.empty():
            min_cost,v = queue.get()
            if d[v]<min_cost:continue

            for to_node,cost in adj_map[v]:
                if d[to_node]>d[v]+cost:
                    d[to_node] = d[v]+cost
                    queue.put((d[to_node],to_node))

        return d
时间复杂度:O(E*logV)

有问题,可以私信我~ 😊


北语张益达
6 声望4 粉丝