📅  最后修改于: 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算法具有广度优先搜索和贪心策略的特点。其基本思想是:从源节点开始,依次按节点到源节点的距离从小到大加入最短路径集合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算法是一种基于动态规划的计算最短路径的算法。其基本思想是:对所有从源节点开始的长度为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*算法是一种启发式搜索算法,结合了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是源节点到目标节点的最短距离。