算法:2-3平衡树与B树的详细探讨

2-3树是最简单的B树,B+树是B树的升级

B树的来源

为什么要有树

  • 描述 1 - 多,N-M、层次等关系
  • 从最根本的原因来看,使用树结构是为了提升整体的效率:插入、删除、查找(索引),尤其是索引操作。因为相比于链表,一个平衡树的索引时间复杂度是 O ( l o g n ) O(log_n) O(logn), 而数组的索引时间复杂度是 O ( n ) O(n) O(n)
    在这里插入图片描述
  • 例子:数据库索引、文件索引等一般都是用树形结构存储的

第一种树:从bst树谈起

我们学的第一个树结果一般都是二叉查找树(BST)

特点:右子节点的数据要比根节点的大,左子节点的数据比根节点小,特殊情况下会变成一个链表。

二叉搜索树其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度(O(log2n))同时也由此而决定。但是,如果插入节点时,插入元素序列本身就是有序的,那么BST树就退化成一个线性表了,操作的时间复杂度为O(n), 之所以会变成O(n),是因为树的高度变大了,BST的比较次数最大是等于树的高度的。因此,如果想要减少比较次数,就需要降低树的高度。
在这里插入图片描述

为了解决上面的问题,我们引入了二叉搜索树

引入第二种树:avl树

构建二叉查找树时,当插入或者删除数据的时候,可能造成树的不平衡, 这个时候,我们可以通过旋转节点来调整树的高度,使得每个节点的左右两个子树的高度差的决定之不超过1(为什么要让高度差不超过1?这样才能保证整棵树的深度最小),这样的树叫做平衡二叉查找树(Self-balancing binary search tree)

平衡二叉搜索树又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log(n)),大大降低了操作的时间复杂度。

在这里插入图片描述

  • 由于每次插入或删除节点后,都可能会破坏AVL的平衡,而要动态保证AVL的平衡需要很多操作,这些操作会影响整个数据结构的性能,除非是在树的结构变化特别少的情形下,否则AVL树平衡带来的搜索性能提升有可能还不足为了平衡树所带来的性能损耗。

为了解决 上面的问题,我们引入了红黑树

红黑树

--------------------------

像是红黑树、avl树等都是二叉树, 在二叉树的查找节点少的情况下,二叉树的操作效率较高。但是如果 二叉 树的节点很多( 海量数据存在数据库或文件中 ) , 又会出现新的问题:

  • 节点海量,会造成二叉树的高度很大,从而降低操作速度
  • 二叉树需要加载到内存的, 在构建二叉树时,需要多次进行 i/o 操作 (不同存储器间的数据读取称为I/O
  • 一般来说,我们操作大的数据都是存储在内存中的,而内存空间是有限的, 如果操作数据量大,内存放不下的时候,我们必须把一部分数据存放在外存中。
  • 对于外存中的数据,常见如数据库,我们通常通过索引表进行数据查找和交互,一般来说,索引表本身也很大,因为数据库中的数据非常多,因此索引也不可能全部存储在内存中,Mysql的MyISAM引擎的索引文件和数据文件是分离的,一张数据库表就有它对应的索引表,索引表中一个索引对应一条数据库记录的磁盘地址,内存中发起请求通过指定索引的查找索引表即可定位唯一的一条数据。
  • 因为索引文件同样存储在磁盘上,这样的话,索引查找过程中每查找一次就要产生一次磁盘I/O消耗,相对于CPU存取,I/O存取的消耗要高几个数量级,访问磁盘的成本大概是访问内存的十万倍左右。
  • 实际上,考虑到磁盘IO是非常高昂的操作,计算机操作已经系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。
  • 在磁盘中读写1B的时间几乎与1KB一样快。同样,批量式访问:以页或块为单位,使用缓冲区。在涉及频繁处理大数据量的算法时,我们要么一次性若干个KB,要么一个字节也不访问
  • 每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k大小的连续磁盘块,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助,通常,索引节点的大小被设计为一页的大小。
  • 对于一旦涉及到这样的外部存储设备(外存),关于时间复杂度的计算就会发生变化,访问某个表/集合元素的时间已经不仅仅是寻找该元素所需比较次数的函数,我们必须考虑对硬盘IO进行操作的时间。由于IO耗时远大于CPU耗时,所以此处评价一个数据结构作为索引的优劣最重要的指标就是要尽量减少查找过程中磁盘IO的存取次数
  • 二叉树一个节点可以有多个孩子,但是它自身只存储一个元素,在元素非常多的时候,就使得要么树的度非常大(节点拥有子树的个数的最大值),要么树的高度非常大,甚至两者都必须足够大才行,这就使得IO次数非常多,这显然成了时间效率上的瓶颈,并且由于一次IO读取一个节点的数据,普通二叉树并不能容纳更多的数据,这样又造成了磁盘块空间的浪费。

为了解决上面的问题(主要是减少IO),我们必须将廋高的树变得“矮胖”,一个基本的想法就是:

  • 每个节点存储多个元素
  • 每个节点有更多的孩子

这就是多叉树。

多叉树之B树

在二叉树中,一个节点只能存储一个数据项,最多有两个子节点。在大数据量的情况下,树肯定会很高,此时查个数据对磁盘读个几千上万次那肯定是不行的。所以我们可以通过在一个节点中存放更多的数据项和引用更多的子节点(这样的树叫做N叉树):

  • 这样,树不用很高就可以标识很大的数据量了,检索次数就大大减少了
  • 用这种数据结构去磁盘中存取数据,磁盘IO次数的次数也会很少。

    多叉树矮胖, 二叉树廋高,使得多叉树非常适合存储在磁盘上。它充分利用了磁盘块的预读机制(局部性访问原理:当一个数据被用到时,其附近的数据也通常会马上被使用),通过缓存的方式进一步提升性能,磁盘在读取某一个文件指针的时候,通常会把紧挨着的数据,也全部读到缓存,因为实践证明,当一个文件数据被访问的时候,它周边的数据很快也会被访问。

B树是最常见的N叉树之一,常用于文件系统和数据库索引。

磁盘块和页的大小一般情况下为4KB,所以在B树中一个节点的最大存储数据个数的总大小一般不能超过4KB

  • 扇区:磁盘的最小存储单位;
  • 磁盘块:文件系统读写数据的最小单位;
  • 页:内存的最小存储单位;

【面试被怼】什么是B树?为啥文件/数据库索引要用B树而不用二叉查找树?

B树

B树又叫做平衡多路查找树(查找路径不只两个)

  • 平衡的意思是:
    • 左边和右边分布均匀
    • 从根节点,到每一个最底部的自己点,链路长度一致
  • 多路的意思是相对于二叉树而言的,二叉树就是二路查找树,查找时只有两条路,而B-tree有多条路,即父节点有多个子节点。

在这里插入图片描述
PS:

  • B树多结点多元素的特点是为了能存更多数据,减少io次数。而平衡二叉树才是解决二叉树出现的极端情况。
  • B树是对二叉查找树的改进, B树的设计思想是: 将相关数据尽量集中在一起,以便一次读取多个数据,减少硬盘操作次数。 B树为系统最优化大块数据的读和写操作。B树算法减少定位记录时所经历的中间构成,从而加快读取速度,普遍运用在数据库和文件系统。
  • 和平衡二叉树一样,B树也能以O(logn)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。不同的是,平衡二叉树是一般是用于优化内存中的数据的查找速度的数据结构,B树一般用于优化外存中数据的查找数据的数据结构,普遍运用在数据库索引结构和文件系统。
  • B树和平衡二叉树都是动态平衡的(也就是说失衡的时候会自动恢复平衡),但是恢复平衡的方法是不同的, AVL树是通过旋转来恢复平衡的,而B树是通过节点分裂来维持的

当我们描述一个B树时需要指定它的阶数, 阶数表示了一个节点最多有多少个孩子节点,一般用字母m表示阶数。

m阶B树特征:

  • 对于根节点: 根节点至少要有两个子节点, 其元素个数范围1<= k <= m-1

    • 当元素个数为0时:
      • 当前结构可以看成是空树、链表、结构体…
    • 当元素个数为1时:
      • 如果没有孩子,就是一个普通的节点,不是树
      • 如果有1个孩子,可以把结构看成是一个链表或者不平衡的树
      • 因为B树是平衡二叉搜索树,如果要达到平衡,那么它至少要有两个孩子
    • 推论:B是至少有3个节点:根节点和它的两个孩子
    • 因为当前树是m阶级B树, 对于这种结构的节点,每个节点最多只能有m-1个元素(包括根节点), 有m-1个元素的节点必须有m个子孩子[这样树才能平衡],当前孩子树是这棵树拥有的最大子节点树,这里的m就是当前树的阶,当前树就叫做m阶B树。
      在这里插入图片描述
  • 对于非根非叶节点(中间节点):每个中间节点都包含k-1个元素和k个孩子,其中 m/2 <= k <= m

    m阶B树的非根非叶结点至少要ceil(m/2)个孩子原因

  • 每个节点都存有索引和数据,也就是对应的key和value。

  • 每个节点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。

  • 树高平衡,也就是所有的叶节点都在同一层,或者说根节点到每个叶子节点的长度都相同。

  • B树把值接近的相关记录放在同一个磁盘页中,从而利用了访问的局部性原理。

比如,5阶的B树,那么根节点数量范围可能时:1 <= k <= 4, 非根节点数量范围:2 <= k <= 4。

ps :

  • 度数:在树中,每个节点的子节点(子树)的个数就称为该节点的度(degree)。

  • 阶数:(Order)阶定义为一个节点的子节点数目的最大值。(自带最大值属性)

B树的相关操作

B树插入

插入的时候,记住一个原则: 判断当前节点key的个数是否小于等于m-1,如果满足,直接插入即可,如果不满足,用节点的中间的key将这个节点分为两部分,

例子:在5阶B树中,结点最多有4个key,最少有2个key(注意:下面的节点统一用一个节点表示key和value)

  • 插入18,70,50,40
    在这里插入图片描述
  • 插入22
    在这里插入图片描述

插入22时,发现这个节点的关键字已经大于4了,所以需要进行分裂,分裂之后,如下
在这里插入图片描述

  • 接着插入23,25,39
    在这里插入图片描述
    分裂,得到下面的
    在这里插入图片描述
    --

B树删除

  • 初始B树(可以看出下面是一个4阶B树, 非叶子节点必须key个数[4/2, 4-1] --> [2, 3])
    在这里插入图片描述
  • 删除15,这种情况是删除叶子节点的元素,如果删除之后,节点数2 还是还在范围内[M/2, M-1],这种情况只要直接删除即可
    在这里插入图片描述
    在这里插入图片描述
  • 接着,我们把22删除,这种情况的规则:22是非叶子节点,对于非叶子节点的删除,我们需要用后继key(元素)覆盖要删除的key,然后在后继key所在的子支中删除该后继key。对于删除22,需要将后继元素24移到被删除的22所在的节点。
    在这里插入图片描述
    在这里插入图片描述
    此时发现26所在的节点只有一个元素,小于2个(m/2),这个节点不符合要求,这时候的规则(向兄弟节点借元素):如果删除叶子节点,如果删除元素后元素个数少于(m/2),并且它的兄弟节点的元素大于(m/2),也就是说兄弟节点的元素比最少值m/2还多,将先将父节点的元素移到该节点,然后将兄弟节点的元素再移动到父节点。这样就满足要求了。

我们看看操作过程就更加明白了。
在这里插入图片描述

  • 接着删除28,删除叶子节点,删除后不满足要求,所以,我们需要考虑向兄弟节点借元素,但是,兄弟节点也没有多的节点(2个),借不了,怎么办呢?如果遇到这种情况,首先,还是将先将父节点的元素移到该节点,然后,将当前节点及它的兄弟节点中的key合并,形成一个新的节点。
    在这里插入图片描述
    移动之后,跟兄弟节点合并。

在这里插入图片描述

最简单的B树:2-3树

2-3树是最简单的B树结构。所谓2-3树,即满足二叉搜索树的性质,且节点可以存放一个元素或者两个元素,每个非叶子节点有两个或三个孩子的树(意思是查找路径可能有2个,可能有3个),而且所有叶都在统一层上。

  • 性质1:满足二叉搜索树的性质。
  • 性质2:节点可以存放一个或两个元素
  • 性质3:每个节点有两个或三个子节点
  • 性质4:因为2-3树是一个B树,B树又叫做平衡二叉查找树, 所以2-3树是一颗绝对平衡的树: 从根节点到任意叶子节点所经过的节点相同, 即2-3 树的所有叶子节点都在同一层, 是 B 树都满足这个条件 )

在这里插入图片描述

2-3树存在以下两种节点:2-节点(存在两个子节点)和3-节点(存在3个子节点)

  • 每个节点的key从左到右保持了从小到大的顺序,两个key之间的子树中所有的key一定大于它的父节点的左key,小于父节点的右key
    在这里插入图片描述

2-3树的创建

两条规则:

  • 规则1: 不能往空的子树上新增元素
  • 规则2: 四节点可以被分解三个2-节点组成的树,并且分解后新树的根节点需要向上和父节点融合
    • 2-3可以看成是一个3阶的B树, 也就是说
      • 每个节点最多可以有2个数据,当插入数据之后,如果发现节点的树等于3的时候,就要开始分裂当前节点数了
      • 对于非叶子节点,每个节点至少要有3/2=1个数据。
示例1

在这里插入图片描述

  • α,向节点1插入数据2,此时为了保持平衡,不会新产生分支,只会在一个节点中存放两个节点。
  • β,继续插入数据3,此时这个节点有三数据,1、2、3,是一个临时区域。
  • γ,把三个数据的节点,中间节点拉起来,调整成树形结构。
  • δ,继续插入数据4,为了保持树平衡,会插在节点3的右侧。
  • ε,继续插入数据5,插入后3、4、5共用1个节点,当一个节点上有三个数据时候,则需要进行调整。
  • ζ,中间节点4向上⏫调整,调整后,1节点在左、3节点在中间、5节点在右。
  • η ,继续插入数据6,在保持树平衡的情况下,与节点5公用。
  • θ ,继续插入数据7,插入后,节点7会与当前的节点 5 6 共用。此时是一个临时存放,需要调整。初步调整后,抽出6节点,向上存放,变为2 4 6共用一个节点,这是一个临时状态,还需要继续调整。
  • ι,因为根节点有三个数据2、4、6,则继续需要把中间节点上移,1、3和5、7 则分别成二叉落到节点2、节点6上。
示例2

数据:[42,37,18,12,11,6,5]

1、使用42初始化根节点
在这里插入图片描述

2、加入节点37: 如果按照一般的二叉搜索树的规则,37应该添加到42的左子树上,但是创建2-3树的时候,规定了不能往空的子树上新增元素, 因此我们需要把37和42继续融合,使之成为一个3节点

在这里插入图片描述

3、加入节点18: 同样,18与[37, 42]进行融合,使之成为一个4节点

在这里插入图片描述
4、添加18之后,出现了4节点,这个时候我们要将4节点拆分成为一颗新的树
在这里插入图片描述
5、插入12: 同样,节点12需要和18节点融合
在这里插入图片描述
6、插入11:
在这里插入图片描述
此时当前节点key数达到了3,需要拆分当前节点
在这里插入图片描述
拆分节点之后,树失去了平衡,我们需要将拆分后的子树的根节点向上融合
在这里插入图片描述
7、插入节点6
在这里插入图片描述

8、插入节点5
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

删除数据

有了上面数据插入的学习,在看数据删除其实就是一个逆向的过程,在删除的主要包括这样两种情况;

  • 删除了3-节点,也就是包含两个数据元素的节点,直接删除即可,不会破坏树平衡。
  • 删除了2-节点,此时会破坏树平衡,需要将树高缩短或者元素合并,恢复树平衡。
示例1

把数据再从7、6、5、4、3、2、1顺序删除,观察2-3树的结构变化,如下
在这里插入图片描述

  • α,删除节点7,因为节点7只有一个数据元素,删除节点5、6合并,但此时破坏了2-3树的平衡性,需要缩短树高进行调整。
  • β,因为删除节点后,整个树结构不平衡,所以需要缩短树高,调整元素。节点2、4合并,节点1、3分别插入左侧和中间。
  • γ,删除节点6,这个节点是3-节点(可以分出3个叉的意思),删除后不会破坏树平衡,保持不变。
  • δ,删除节点5,此时会破坏树平衡,需要把跟节点4下放,与3合并。
  • ε,删除节点4,这个节点依旧是3-节点,所以不需要改变树结构。
  • ζ,删除节点3,此时只有1、2节点,需要合并。
  • η ,删除节点2,此时节点依旧是3-节点,所以不需要改变树结构。
示例2

在这里插入图片描述

  • 删除4,其实需要将节点3、5合并,指向节点2,保持树平衡。
  • 删除7,节点8、9合并。
  • 删除14,节点15上移,恢复成3-叉树。
总结
  • 删除的是非叶子节点:使用中序遍历下的直接后继节点key来覆盖当前节点key,再删除用来覆盖的后继节点key
    在这里插入图片描述

  • 删除叶子节点

    • 当前节点是3节点(有2个key):直接删除key
      在这里插入图片描述

    • 当前节点是2节点(有1个key):删除节点,然后判断

      • 当前节点的双亲节点是2节点(有1个key),兄弟是3节点(2个key): 将双亲节点移动到当前位置,再将兄弟节点中最接近当前位置的key移动到双亲节点中
        在这里插入图片描述

      • 当前节点的双亲节点是2节点(有1个key),兄弟是2节点(1个key):先通过移动兄弟节点的中序遍历直接后驱到兄弟节点,以使兄弟节点变为3-节点;再进行上面的操作
        在这里插入图片描述

      • 当前节点的双亲节点是3-节点(2个key):拆分双亲节点使其成为2-节点(1个key),再将再将双亲节点中最接近的一个拆分key与中孩子合并,将合并后的节点作为当前节点
        在这里插入图片描述

      • 2-3树是一颗满二叉树,将2-3树层树减少,并将兄弟节点合并到双亲节点中,同时将双亲节点的所有兄弟节点合并到双亲节点的双亲节点中,如果生成了-4节点,再分解4-节点即可
        在这里插入图片描述

查找数据

基本原则:

  • 小于当前节点值,左侧寻找
  • 大于当前节点值,右侧寻找
  • 一直到找到索引值,停止
    在这里插入图片描述


一个节点中有几个孩子就有几条搜索路径

面试官问你B树和B+树,就把这篇文章丢给他
面经手册 · 第5篇《看图说话,讲解2-3平衡树「红黑树的前身」》–看完了
浅析——B树,B+树,B*树以及分析MySQL的两种引擎
2-3树与2-3-4树
数据结构树,为什么是B+树
一步步分析为什么B+树适合作为索引的结构
https://blog.csdn.net/u010916338/article/details/86134334
B树的查找、插入、删除
二叉查找树、平衡二叉树、红黑树、B-/B+树性能对比
https://blog.csdn.net/m0_38075425/article/details/81777364
https://blog.csdn.net/m0_38075425/article/details/81777364
https://blog.csdn.net/weixin_43767015/article/details/105880671?utm_medium=distribute.pc_relevant.none-task-blog-title-4&spm=1001.2101.3001.4242

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Bellman-Ford算法是一种用于解决单源最短路径问题的算法。它可以处理带有负权边的图,并且可以检测到负权环。下面我将给出一个具体的案例来说明Bellman-Ford算法的实现过程。 假设有一个带有负权边的有向图,如下所示: ![Bellman-Ford算法具体案例及算法](https://img-blog.csdnimg.cn/20211201171019322.png) 我们要求从源点S到其它各个顶点的最短路径。首先,我们需要初始化距离数组dist,将源点S的距离设为,其它顶点的距离设为无穷大。同时,我们需要初始化前驱数组prev,将源点S的前驱设为自身,其它顶点的前驱设为null。 接下来,我们需要进行n-1次松弛操作,其中n为图中顶点的个数。每次松弛操作都会遍历图中的所有边,对每条边进行松弛操作。松弛操作的具体过程如下: 对于一条边(u, v),如果dist[u]+w(u, v)<dist[v],则更新dist[v]为dist[u]+w(u, v),同时更新prev[v]为u。 其中,w(u, v)表示边(u, v)的权值。 第一次松弛操作后,距离数组和前驱数组的值如下所示: ![Bellman-Ford算法具体案例及算法](https://img-blog.csdnimg.cn/20211201171019321.png) 第二次松弛操作后,距离数组和前驱数组的值如下所示: ![Bellman-Ford算法具体案例及算法](https://img-blog.csdnimg.cn/20211201171019320.png) 第三次松弛操作后,距离数组和前驱数组的值如下所示: ![Bellman-Ford算法具体案例及算法](https://img-blog.csdnimg.cn/20211201171019319.png) 第四次松弛操作后,距离数组和前驱数组的值如下所示: ![Bellman-Ford算法具体案例及算法](https://img-blog.csdnimg.cn/20211201171019318.png) 第五次松弛操作后,距离数组和前驱数组的值如下所示: ![Bellman-Ford算法具体案例及算法](https://img-blog.csdnimg.cn/20211201171019317.png) 最终,我们得到了从源点S到其它各个顶点的最短路径。其中,如果距离数组中存在负数,则说明图中存在负权环,Bellman-Ford算法无法处理这种情况。 以上就是Bellman-Ford算法的具体案例及算法。 ### 回答2: Bellman-Ford算法是解决图中单源最短路径问题的一种算法。它可以用于有向图和带有负权边的图。 算法具体案例如下:假设有一个有向图,其中包含5个节点(A、B、C、D、E)和6条边,边的权重如下:AB(6),AC(3),BC(2),BD(5),CD(-2),DE(1)。 Bellman-Ford算法如下: 1. 初始化距离数组dist[],将起点到所有其他点的距离初始化为无穷大,除了起点本身的距离初始化为0。 2. 对于每个节点进行n-1次迭代,其中n是节点的数量。(本例中进行4次迭代) 3. 在每次迭代中,遍历所有的边,如果从当前节点u到边的终点v的距离加上边的权重w小于dist[v],则更新dist[v]为新的距离值。 4. 完成n-1次迭代后,得出的dist[]数组中存储的就是起点到其他节点的最短路径距离。 在本例中,按照上述算法进行4次迭代后,得到的最终dist[]数组为[0, 3, 5, 7, 8],表示起点A到其他节点的最短路径距离分别为:A到B为0,A到C为3,A到D为5,A到E为7。 Bellman-Ford算法的时间复杂度为O(V * E),其中V是节点数量,E是边数量。这是由于该算法需要遍历所有的边,并进行n-1次迭代。当出现负权环的情况时,算法无法得出最短路径结果。但可以通过检测负权环的存在来判断是否存在该问题。 ### 回答3: Bellman-Ford算法是一种用于计算带负权边的图中单源最短路径的算法。它可以解决一般的有向图和带有负权边的图的最短路径问题。算法的时间复杂度为O(VE),其中V是图中的顶点数,E是图中的边数。 下面以一个具体案例来说明Bellman-Ford算法的执行过程: 假设有一个有向图,其中包含5个顶点和6条边。我们要求从顶点A到其他所有顶点的最短路径。 顶点 边 权重 A AB 1 A AC -3 B CD 2 C BD -2 D AE 3 E BC 2 首先,我们初始化源顶点A到其他所有顶点的距离为无穷大,除了A自身到A的距离为0。同时,我们初始化一个辅助数组distance用于存储当前已知的最短距离。 然后,我们开始执行Bellman-Ford算法的主循环。在每一轮循环中,我们遍历所有边,计算通过当前边能够获得的更短路径。如果发现了一条更短的路径,则更新距离数组distance。一共需要执行V-1轮循环,其中V是图中的顶点数。 第一轮循环: 假设distance[A] = 0,distance[B] = ∞,distance[C] = ∞,distance[D] = ∞,distance[E] = ∞。 遍历边AB,发现distance[B] > distance[A] + 1,更新distance[B] = distance[A] + 1 = 1。 遍历边AC,发现distance[C] > distance[A] - 3,更新distance[C] = distance[A] - 3 = -3。 其他边没有更新。 第二轮循环: 假设distance[A] = 0,distance[B] = 1,distance[C] = -3,distance[D] = ∞,distance[E] = ∞。 遍历边CD,发现distance[D] > distance[C] + 2,更新distance[D] = distance[C] + 2 = -1。 遍历边BD,发现distance[D] > distance[B] - 2,更新distance[D] = distance[B] - 2 = -1。 其他边没有更新。 第三轮循环: 假设distance[A] = 0,distance[B] = 1,distance[C] = -3,distance[D] = -1,distance[E] = ∞。 遍历边AE,发现distance[E] > distance[A] + 3,更新distance[E] = distance[A] + 3 = 3。 其他边没有更新。 第四轮循环: 假设distance[A] = 0,distance[B] = 1,distance[C] = -3,distance[D] = -1,distance[E] = 3。 遍历边BC,发现distance[C] > distance[B] + 2,更新distance[C] = distance[B] + 2 = 3。 其他边没有更新。 最后,经过V-1=4轮循环,我们得到了从顶点A到其他所有顶点的最短路径。结果为distance[A] = 0,distance[B] = 1,distance[C] = 3,distance[D] = -1,distance[E] = 3。 Bellman-Ford算法的核心思想是通过松弛操作不断更新当前已知的最短距离,直到达到最优解。当算法结束后,如果存在从源点可达的顶点无法通过松弛操作更新,则说明图中存在负权回路。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值