Fancy DSA Fancy DSA
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)

Jonsam NG

想的更多,也要想的更远
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)
  • 开始上手
  • Plan 计划
  • Roadmap 路线
  • 算法简介
  • Sort 排序

  • Search 搜索

  • Recursive 递归

  • Graph 图

    • BellmanFord [贝尔曼-福特算法]
      • 介绍
      • 原理
      • 优化
      • 演示
      • 实现
      • 参考
    • BreadthFirstSearch [广度优先搜索/BFS]
    • BreadthFirstShortestPath [广度优先寻最短路径]
    • ConnectedComponents [连通元件]
    • Density [图的密度]
    • DepthFirstSearch [深度优先搜索]
    • Dijkstra [迪杰斯特拉算法]
    • FloydWarshall [弗洛伊德算法]
    • KruskalMST [克鲁斯克尔算法]
    • NodeNeighbors [节点邻域]
    • NumberOfIslands [岛屿数量]
    • PrimMST [普林姆算法]
  • Tree 树

  • Math 数学

  • Hash 哈希

  • String 字符串

  • BitManipulation 位操纵

  • Backtracking 回溯

  • DynamicProgramming 动态规划

  • Cache 缓存

  • Array 数组

  • Ciphers 密码学

  • Conversions 转换

  • ProjectEuler 欧拉计划

  • 其他

  • 算法
  • Graph 图
jonsam
2022-05-01
目录

BellmanFord [贝尔曼-福特算法]

# 介绍

贝尔曼 - 福特算法(英语:Bellman–Ford algorithm),求解单源最短路径问题的一种算法。它的原理是对图进行∣V∣−1{\displaystyle |V|-1}∣V∣−1 次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达O(∣V∣∣E∣)O(|V||E|)O(∣V∣∣E∣)。但算法可以进行若干种优化,提高了效率。

Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法。该算法由 Richard Bellman 和 Lester Ford 分别发表于 1958 年和 1956 年,而实际上 Edward F. Moore 也在 1957 年发布了相同的算法,因此,此算法也常被称为 Bellman-Ford-Moore 算法。

Bellman-Ford 算法和 Dijkstra 算法同为解决单源最短路径的算法。对于带权有向图 G = (V, E),Dijkstra 算法要求图 G 中边的权值均为非负,而 Bellman-Ford 算法能适应一般的情况(即存在负权边的情况)。一个实现的很好的 Dijkstra 算法比 Bellman-Ford 算法的运行时间要低。

Bellman-Ford 算法采用动态规划(Dynamic Programming)进行设计,实现的时间复杂度为 O (V*E),其中 V 为顶点数量,E 为边的数量。Dijkstra 算法采用贪心算法(Greedy Algorithm)范式进行设计,普通实现的时间复杂度为 O (V2),若基于 Fibonacci heap 的最小优先队列实现版本则时间复杂度为 O (E + VlogV)。

Bellman-Ford 算法描述:

  • 创建源顶点 v 到图中所有顶点的距离的集合 distSet,为图中的所有顶点指定一个距离值,初始均为 Infinite,源顶点距离为 0;
  • 计算最短路径,执行 V - 1 次遍历;
  • 对于图中的每条边:如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d;
  • 检测图中是否有负权边形成了环,遍历图中的所有边,计算 u 至 v 的距离,如果对于 v 存在更小的距离,则说明存在环;

# 原理

  • 松弛:每次松弛操作实际上是对相邻节点的访问,第 n 次松弛操作保证了所有深度为 n 的路径最短。由于图的最短路径最长不会经过超过∣V∣−1{\displaystyle |V|-1}∣V∣−1 条边,所以可知贝尔曼 - 福特算法所得为最短路径。
  • 负边权操作:与迪科斯彻算法不同的是,迪科斯彻算法的基本操作 “拓展” 是在深度上寻路,而 “松弛” 操作则是在广度上寻路,这就确定了贝尔曼 - 福特算法可以对负边进行操作而不会影响结果。
  • 负权环判定:因为负权环可以无限制的降低总花费,所以如果发现第 n 次操作仍可降低花销,就一定存在负权环。

# 优化

  • 循环的提前跳出:在实际操作中,贝尔曼 - 福特算法经常会在未达到 V - 1 次前就出解,其实 V - 1 是最大值。于是可以在循环中设置判定,在某次循环不再进行松弛时,直接退出循环,进行负权环判定。
  • 队列优化:西南交通大学的段凡丁于 1994 年提出了用队列来优化的算法。松弛操作必定只会发生在最短路径前导节点松弛成功过的节点上,用一个队列记录松弛过的节点,可以避免了冗余计算。原文中提出该算法的复杂度为 O (k|E|),是个比较小的系数,但该结论未得到广泛认可。参见:最短路径快速算法_百度百科 (opens new window)

# 演示

参考:

  • The Bellman-Ford Algorithm (opens new window)
  • BELLMAN-FORD DEMO (opens new window)

# 实现

# JavaScript

/*
The Bellman–Ford algorithm is an algorithm that computes shortest paths
from a single source vertex to all of the other vertices in a weighted digraph.
It also detects negative weight cycle.

Complexity:
    Worst-case performance O(VE)
    Best-case performance O(E)
    Worst-case space complexity O(V)

Reference:
    https://en.wikipedia.org/wiki/Bellman–Ford_algorithm
    https://cp-algorithms.com/graph/bellman_ford.html

*/

/**
 *
 * @param graph Graph in the format (u, v, w) where
 *  the edge is from vertex u to v. And weight
 *  of the edge is w.
 * @param V Number of vertices in graph
 * @param E Number of edges in graph
 * @param src Starting node
 * @param dest Destination node
 * @returns Shortest distance from source to destination
 */
function BellmanFord (graph, V, E, src, dest) {
  // Initialize distance of all vertices as infinite.
  const dis = Array(V).fill(Infinity)
  // initialize distance of source as 0
  dis[src] = 0

  // Relax all edges |V| - 1 times. A simple
  // shortest path from src to any other
  // vertex can have at-most |V| - 1 edges
  for (let i = 0; i < V - 1; i++) {
    for (let j = 0; j < E; j++) {
      if ((dis[graph[j][0]] + graph[j][2]) < dis[graph[j][1]]) { 
        dis[graph[j][1]] = dis[graph[j][0]] + graph[j][2] 
      }
    }
  }
  // check for negative-weight cycles.
  for (let i = 0; i < E; i++) {
    const x = graph[i][0]
    const y = graph[i][1]
    const weight = graph[i][2]
    if ((dis[x] !== Infinity) && (dis[x] + weight < dis[y])) {
      return null
    }
  }
  for (let i = 0; i < V; i++) {
    if (i === dest) return dis[i]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 参考

  • 贝尔曼 - 福特算法 - 维基百科,自由的百科全书 (opens new window)
  • Bellman-ford 算法 - 知乎 (opens new window)
  • 贝尔曼 - 福特算法_百度百科 (opens new window)
  • 算法系列 —— 贝尔曼福特算法(Bellman-Ford) (opens new window)
编辑 (opens new window)
上次更新: 2022/10/17, 12:47:34
TowerOfHanoi [汉诺塔]
BreadthFirstSearch [广度优先搜索/BFS]

← TowerOfHanoi [汉诺塔] BreadthFirstSearch [广度优先搜索/BFS]→

最近更新
01
0-20题解
10-31
02
本章导读
10-31
03
算法与转换:Part1
10-28
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Fancy DSA | Made by Jonsam by ❤
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式