📜  单一来源最短路径(1)

📅  最后修改于: 2023-12-03 14:50:27.983000             🧑  作者: Mango

单一来源最短路径

简介

单一来源最短路径问题指:给定一个有向加权图G=(V,E),其中边权值可以为负数,源节点s和目的节点t,求从s到t的一条总权值最小的路径P。最短路径问题是图论中的经典问题之一,能够应用于路由选择、物流配送、通信传输等领域。

单一来源最短路径问题的算法包括Dijkstra算法、Bellman-Ford算法和A算法等,其中Dijstra算法适合处理边权值为正的图;Bellman-Ford算法能够处理边权值为负的图;而A算法则是一种启发式搜索算法,可以在求解最短路径问题的同时,实现更高效的搜索。

Dijkstra算法

Dijkstra算法具有广度优先搜索和贪心策略的特点。其基本思想是:从源节点开始,依次按节点到源节点的距离从小到大加入最短路径集合S中,直到达到目标节点或者所有的节点都被加入集合S为止。具体实现可采用堆优化的方式,提高算法效率。

下面是Dijkstra算法的伪代码:

1. 初始化:把源节点加入集合S中,把源节点到每个顶点的距离初始化为无穷大。
2. 把源节点到自身的距离初始化为0。
3. 建立一个优先队列,把源节点加入队列中。
4. 当队列不为空时,依次取出距离源节点最近的一个节点v,把v加入集合S中。
5. 对v的每个邻接节点u进行松弛操作,更新从源节点到u的距离值。
6. 把新加入集合S中的节点的关键字值加入优先队列中,以保持队列有序。
7. 重复步骤4-6,直到队列为空或者遍历到目标节点。

使用Python实现Dijkstra算法的代码片段如下:

import heapq

def dijkstra(graph, start, end):
    distances = {v: float('inf') for v in graph}
    distances[start] = 0
    pq = [(0, start)]
    while pq:
        curr_dist, curr_node = heapq.heappop(pq)
        if curr_dist > distances[curr_node]:
            continue
        for neighbor, edge_weight in graph[curr_node].items():
            distance = curr_dist + edge_weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heapq.heappush(pq, (distance, neighbor))
        if curr_node == end:
            break
    return distances[end]

其中,graph是一个邻接字典,start和end分别是源节点和目标节点。时间复杂度为O(E+VlogV),其中E表示边数,V表示顶点数。

Bellman-Ford算法

Bellman-Ford算法是一种基于动态规划的计算最短路径的算法。其基本思想是:对所有从源节点开始的长度为k的简单路径,依次进行松弛操作,直到所有的长度为k的路径都被遍历过并更新完毕,或者发现存在负权环。

下面是Bellman-Ford算法的伪代码:

1. 初始化:把源节点到各个节点的距离初始化为无穷大,把源节点的距离初始化为0。
2. 对所有边进行k-1轮松弛操作,其中k为路径的长度,即k<=|V|-1。
3. 检测负权环,如果存在负权环,则算法会陷入死循环,可以通过判断松弛操作的次数是否达到了|V|-1次来判断是否存在负权环。

使用Python实现Bellman-Ford算法的代码片段如下:

def bellman_ford(graph, start, end):
    distances = {v: float('inf') for v in graph}
    distances[start] = 0
    for i in range(len(graph) - 1):
        for u, edges in graph.items():
            for v, w in edges.items():
                if distances[u] != float('inf') and distances[u] + w < distances[v]:
                    distances[v] = distances[u] + w
    for u, edges in graph.items():
        for v, w in edges.items():
            if distances[u] != float('inf') and distances[u] + w < distances[v]:
                return None
    return distances[end]

其中,graph是一个邻接字典,start和end分别是源节点和目标节点。时间复杂度为O(VE),其中E表示边数,V表示顶点数。

A*算法

A*算法是一种启发式搜索算法,结合了Dijkstra算法和贪心算法的优点,能够求解最短路径问题,并实现更高效的搜索。其基本思想是:在搜索过程中,对每个节点引入估价函数,通过该函数计算每个节点离目标节点的估计距离,并以此为基础实现有优先级的搜索。

下面是A*算法的伪代码:

1. 初始化:把源节点加入开启列表open中,把目标节点加入关闭列表close中,并把源节点到自身的估计距离初始化为0。
2. 从开启列表中找到估计距离f(x)值最小的节点x,加入关闭列表中。
3. 对节点x的所有邻接节点u,计算从源节点到u经过节点x的路径长度,并更新节点u的距离值和父节点。
4. 对更新后的每个节点u,计算从u到目标节点的估计距离h(u),并计算其估计距离f(u)=h(u)+g(u)。
5. 把开启列表open中已经加入关闭列表close的节点删除,把新的节点加入开启列表open。
6. 重复步骤2-5,直到目标节点加入关闭列表close中或者开启列表为空。

使用Python实现A*算法的代码片段如下:

import heapq

def heuristic(a, b):
    return abs(a[0] - b[0]) + abs(a[1] - b[1])

def astar(array, start, end):
    open_nodes = []
    closed_nodes = set()
    start_node = (0, start)
    heapq.heappush(open_nodes, start_node)
    while open_nodes:
        curr_node = heapq.heappop(open_nodes)
        if curr_node[1] == end:
            path = []
            while curr_node[1] != start:
                path.append(curr_node[1])
                curr_node = curr_node[2]
            path.append(start)
            return path[::-1]
        closed_nodes.add(curr_node[1])
        neighbors = ((0, 1), (0, -1), (1, 0), (-1, 0))
        for neighbor in neighbors:
            neighbor_node = (curr_node[0] + 1 + heuristic(end, (curr_node[1][0] + neighbor[0], curr_node[1][1] + neighbor[1])), (curr_node[1][0] + neighbor[0], curr_node[1][1] + neighbor[1]), curr_node)
            if neighbor_node[1][0] < 0 or neighbor_node[1][1] < 0 or neighbor_node[1][0] >= len(array) or neighbor_node[1][1] >= len(array[0]) or array[neighbor_node[1][0]][neighbor_node[1][1]] == 1 or neighbor_node[1] in closed_nodes:
                continue
            heapq.heappush(open_nodes, neighbor_node)
    return None

其中,array是表示地图的二维数组,0表示可走,1表示障碍物;start和end分别是源节点和目标节点。时间复杂度取决于启发式函数,一般情况下是O(b^d),其中b是每个节点的平均分支数,d是源节点到目标节点的最短距离。