单源加权图最短路径问题2-BellmanFord算法

最短路径算法—Bellman-Ford(贝尔曼-福特)算法分析与实现(CC++) 最短路径算法—Bellman-Ford(贝尔曼-福特)算法分析与实现(CC++),希望对你能有所帮助! 立即下载

之前提到Dijkstra算法不能解决权值为负的情况。    

Bellman-Ford算法能在更一般的情况下解决最短路径问题,即:允许权值为负。
注意,最短路径问题实际上均不允许有负值回路(当然是从源点可达的),因为这时不存在最短路径。总会有更短的办法-多绕负边回路走几趟就是了。
    
首先介绍一下松弛技术(Relaxation)
对每个顶点,都设置一个属性d[v], 用来描述从源点s到v的最短路径上权值的上界。
一步松弛操作的结果可以看作是对约束 d[v]<=d[u] + w(u,v)的松弛,即:每次比较dv和du+w(u,v)及更新dv叫做一次 松弛操做。        
通俗来说,松弛可以认为是求最短路径时,每一次优化路径的过程。
总结来看,引入松弛概念其实主要是为了抽象一类步骤优化操作,为了思考问题,写伪代码方便。比如Dijkstra写成含有Relax的算法形式为:
        void Dijkstra(Graph& g, VertexNode& s)
        {
            for each vertex v in g
            {
                v.distance = INFINITY;
                v.known = false;
                v.path = NULL;
            }
            s.distance = 0;
            for (int i=0; i<g.vertexnum; i++)
            {
                vertex v = smallest unknown distance vertex;
                if (v==NULL)
                    break;
                else
                    v.known = true;
                for each w adjacent to v
                {
                    if (!w.known)                    
                    {
                         Relax(w,v);                    
                    }
                }
            }
        }
    这样,Relax部分优化操作可以独立思考具体书写步骤。      
 
回到原话题,Dijkstra算法实际上相当于对每条边执行一次Relax操作,所以其算法复杂度为O(|E|+×)。也就是说,顶点known标记的改变,记录了每条边的松弛操作。而对于含有负边的图,known顶点会出现标记过早的情况,即一次Relax操作是不够的,所以Dijkstra算法不起作用。
Bellman-Ford算法的核心则是对每条边执行多次Relax操作,从而可以取消known的标记,因为顶点的known意向可能被之后的Relax操作改变。
Relax操作有一个非常重要的性质-路径松弛性质:如果p<v0,v1,v2,...,vk>是一个从v0到vk的最短路径,而p的边是按照<v0,v1>,<v1,v2>...<vk-1,vk>的顺序进行松弛的,那么d[vk]就是最短路径长度。这个性质的保持不受其他松弛操作的影响,即使他们与p的边上的松弛操作混在一起。
利用这个性质,我们很容易得出结论:对于一个图所有边均进行|V|-1次松弛操作,一定能得到最短路径。
    所以可以得到伪代码:
    BellmanFord(g, s)
    {
        for (int i=0; i < g.VertexNum; i++)
            for each edge(u, v) in g
                Relax(u, v);
    }
    算法复杂度为O(|E||V|)
    另外还有一种利用类似广度优先搜索算法实现该算法:
    这里其实相当于对上面的算法做了一个优化:如果某次对所有边进行Relax操作,没有任何d值变化,则可以立即退出迭代而不需要|V|-1次迭代都做完。
    void BellmanFord(Graph& g, Vertex& s)
    {
        queue<vertex> q;
        for each vertex v in g
        {
           v.distance = INFINITY;
           v.path = NULL;
        }
        s.distance = 0;
        q.enqueue(s);
        while (!q.Empty())
        {
           v = dequeue(q);
           for each w adjenct to v
           {
               Relax(w,v)
               {   
                
                   if (w not in q)
                       q.enqueue(w);
               }
           }
        }   
    }

 

 代码实现:
#include <iostream>
#include <deque>
#include <algorithm>

using namespace std;

#define MAX_VERTEX_NUM    20
#define INFINITY    2147483647
struct adjVertexNode
{
    int adjVertexPosition;
    int weight;
    adjVertexNode * next;
};
struct VertexNode
{
    char data [ 2 ];
    int distance;
    VertexNode * path;
    adjVertexNode * list;
};
struct Graph
{
    VertexNode VertexNode [ MAX_VERTEX_NUM ];
    int vertexNum;
    int edgeNum;
};

void CreateGraph ( Graph & g)
{
     int i , j , edgeStart , edgeEnd , edgeWeight;
     adjVertexNode * adjNode;
     cout << "Please input vertex and edge num (vnum enum):" << endl;
     cin >> g . vertexNum >> g . edgeNum;
     cout << "Please input vertex information (v1) /n note: every vertex info end with Enter" << endl;
     for ( i = 0; i < g . vertexNum; i ++)
     {
         cin >> g . VertexNode [ i ]. data; // vertex data info.
         g . VertexNode [ i ]. list = NULL;
     }
     cout << "input edge information(start end weight):" << endl;
     for ( j = 0; j < g . edgeNum; j ++)
     {
         cin >> edgeStart >> edgeEnd >> edgeWeight;
         adjNode = new adjVertexNode;
         adjNode -> weight = edgeWeight;
         adjNode -> adjVertexPosition = edgeEnd - 1; // because array begin from 0, so it is j-1
         // 将邻接点信息插入到顶点Vi的边表头部,注意是头部!!!不是尾部。
         adjNode -> next = g . VertexNode [ edgeStart - 1 ]. list;
         g . VertexNode [ edgeStart - 1 ]. list = adjNode;
     }
}

void PrintAdjList( const Graph & g)
{
    for ( int i = 0; i < g . vertexNum; i ++)
    {
        cout << g . VertexNode [ i ]. data << "->";
        adjVertexNode * head = g . VertexNode [ i ]. list;
        if ( head == NULL)
            cout << "NULL";
        while ( head != NULL)
        {
            cout << head -> adjVertexPosition + 1 << " ";
            head = head -> next;
        }
        cout << endl;
    }
}
void DeleteGraph( Graph & g)
{
    for ( int i = 0; i < g . vertexNum; i ++)
    {
        adjVertexNode * tmp = NULL;
        while( g . VertexNode [ i ]. list != NULL)
        {
            tmp = g . VertexNode [ i ]. list;
            g . VertexNode [ i ]. list = g . VertexNode [ i ]. list -> next;
            delete tmp;
            tmp = NULL;
        }
    }
}
void BellmanFord( Graph & g , VertexNode & s)
{
    deque < VertexNode *> q;
    for ( int i = 0; i < g . vertexNum; i ++)
    {
        g . VertexNode [ i ]. distance = INFINITY;
        g . VertexNode [ i ]. path = NULL;
    }
    s . distance = 0;
    q . push_back( &s);
   
    int counter = 0;
    while( ! q . empty())
    {
        VertexNode * v = q . front();
        q . pop_front();
        if( v == NULL)
            break;
       
        adjVertexNode * head = v -> list;
        while ( head != NULL)
        {
            VertexNode * w = & g . VertexNode [ head -> adjVertexPosition ];
            if( v -> distance + head -> weight < w -> distance)
            {
                w -> distance = v -> distance + head -> weight;
                w -> path = v;
                if ( find( q . begin (), q . end (), w) == q . end())
                {
                    q . push_back( w);
                }
            }
            head = head -> next;
        }
        counter ++;
        if ( counter > g . vertexNum * g . edgeNum)
        {
            cout << "This graph has minus value loop!" << endl;
            exit( 1);
        }
    }
}
void PrintPath( Graph & g , VertexNode * source , VertexNode * target)
{
    if ( source != target && target -> path == NULL)
    {
        cout << "There is no shortest path from " << source -> data << " to " << target -> data << endl;
    }
    else
    {
        if ( target -> path != NULL)
        {
            PrintPath( g , source , target -> path);
            cout << " ";
        }
        cout << target -> data ;
    }
}

int main( int argc , const char ** argv)
{
    Graph g;
    CreateGraph( g);
    PrintAdjList( g);
    VertexNode & start = g . VertexNode [ 0 ];
    VertexNode & end = g . VertexNode [ 6 ];
    BellmanFord( g , start);
    cout << "print the shortest path from v1 to v7" << endl;
    PrintPath( g , & start , & end);
    cout << endl;
    DeleteGraph( g);
    return 0;
}
     
 其中判断含有负值回路的方法很多,这里采用认为最多会有|V|×|E|次操作,否则存在负值回路。
 使用 queue也是可以的,但要注意 relax 内部判断要写成:

  if (find(q._Get_container().begin(), q._Get_container().end(), w) == q._Get_container().end())
  略显臃肿。 而实际上 queue也是一个container adapter,他内部封装了deque。

 

运行示例:
1 含有负值权边,但不含负值回路。

 

 

运行结果

2 含有负值回路:v6->v7->v6 (-10)

运行结果

Transformer——从原理、模型架构到内部细节介绍 Transformer模型是一种基于注意力机制的深度学习模型,最早由Google在2017年的论文《AttentionIsAllYouNeed》中提出。自其问世以来就迅速席卷了自然语言处理他(NLP)领域,并在各类主流任务上取得了新的突破,包括机器翻译、语言建模、序列标注和文本分类等。在NLP领域取得巨大成功后,迅速扩展到了计算机视觉(CV)领域,并带来了革命性的变化。这种基于自注意力机制的深度学习模型,不仅在NLP任务中力压群雄,也成为CV领域的新宠。 阅读详情

相关推荐

Cocos Creator+微信小游戏:如何用wxDownloader实现资源动态加载与热更新

本文深入探讨了在Cocos Creator开发微信小游戏时,如何利用wxDownloader有效应对主包体积限制,实现资源的动态加载与热更新。文章详细介绍了从项目配置、资源标记到初始化加载的完整流程,并重点解析了基于MD5 Cache的缓存策略与版本控制方案,旨在帮助开发者构建稳健高效的资源管理体系,优化游戏启动速度与更新体验。

css33的专栏 823

最短路径算法—Bellman-Ford(贝尔曼-福特)算法分析与实现(C/C++)

Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。该算法由美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明。Bellma

long-long-ago 1042

Bellman-ford算法详解

Bellman-ford算法详解

qq_52905520的博客 2万+

最邻近点对问题(Closest-Pair Problem):二维的分治解法详解

低维时,最邻近点对问题可以利用分治求解。但是网络上相关资料良莠不齐,对于3维甚至难有有价值的参考。最近恰巧梳理了123维最邻近点对问题的相关知识,故在此分享。这是该系列的第二篇,介绍二维情况的分治解法。

Carl_Rabbit的博客 1万+

CTFshow-CRYPTO(持续更新)

密码学签到 }wohs.ftc{galf 将字符串逆序即可 letters = '}wohs.ftc{galf' a = list(letters) a.reverse() for letters in a: print(letters,end='') crypto2 打开浏览器的控制台,复制粘贴回车即可得到flag crypto3 这一题同上 crypto4 如图填写即可,一开始e填写的是17,一直回答错误,后来经过群内大佬提醒软件的e是十六进制的,17转十六进制就是0x11 cr

i_kei的博客 1万+

Bellman——Ford算法

一.Bellman—Ford算法是用来干什么的 . 我们都知道Dijkstra算法只能用来解决正权图的单源最短路径问题,但有些题目会出现负权图。这时这个算法就不能帮助我们解决问题了,而BellmanFord算法可以解决负权图且可以判断有无负权回路(迭代超过V-1次,就说明存在负权回路)的单源最短路径问题。 . 它也有明显的缺点,它的时间复杂度是要高于Dijkstra算法的。 二.算法流程 . ...

hahahahahaha5的博客 2万+

【Godot4自学手册】第一节配置Godot运行环境

Godot是一款自由开源、由社区驱动的 2D 和 3D 游戏引擎!它是一款功能丰富的跨平台游戏引擎,可以通过统一的界面创建 2D 和 3D 游戏。它提供了一套全面的通用工具,因此用户可以专注于制作游戏,而无需重新发明轮子。游戏可以一键导出到多个平台,包括主流的桌面平台(Linux、macOS、Windows)、移动平台(Android、iOS)、基于 Web 的平台以及主机平台。

zhaoyang314的博客 5984

图详解第五篇:单源最短路径--贝尔曼-福特(Bellman-Ford)算法

单源最短路径--Bellman-Ford算法(可处理带负权路径的图)

m0_70980326的博客 9243

基于Simulink的智能车辆电子稳定控制(ESC)仿真

本文完成了基于 Simulink 的智能车辆电子稳定控制(ESC),实现了:✅ 构建含轮胎非线性的车辆动力学模型✅ 设计滑模横摆稳定性控制器✅ 实现四轮制动力分配逻辑✅ 验证在双移线低附着场景下的卓越稳定性✅ 为主动安全控制系统开发提供完整范式。

xiaoheshang_123的博客 79

基于Keil软件实现对射式红外传感器计次 (江协科技HAL库)

用HAL库复现江协科技,让嵌入式学习之路更顺畅。

2401_82728381的博客 798

算法(五):图解贝尔曼-福特算法

算法简介 贝尔曼-福特算法(Bellman–Ford algorithm )用于计算出起点到各个节点的最短距离,支持存在负权重的情况 它的原理是对图进行最多V-1次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达O(VE)。但算法可以进行若干种优化,提高了效率。 Bellman Ford算法每次对所有的边进行松弛,每次松弛...

weixin_34187822的博客 4370

华为OD机考双机位C卷 - 斗地主之顺子 (Java & Python& JS & C/C++ & GO )

华为OD机考双机位C卷 - 斗地主之顺子(OD上机考试双机位C卷)。提供在线OJ刷题。Java、C++、Python、GO语言、C语言、JavaScript题解,并提供完整思路讲解。

算法大师! 9694

单源最短路径(二)————BellmanFord算法

package om.eshore.sweetop.minpath;public class BellmanFord extends Graphics {        public BellmanFord(Vertex...vs){        for (Vertex v : vs) {            list.add(v);        }    }        @Overrid

独自登高楼 望断天涯路 1630

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

【图论】bellman-ford 算法 + spfa 算法(基于队列优化)单源最短路(code c++)

目录&索引算法原理bellman-ford算法与spfa算法两者关系spfa算法通俗介绍(对比于dijkstra算法)程序代码bellman-ford code c++spfa code c++结论 算法原理 bellman-ford算法与spfa算法两者关系 spfa 算法是 bellman-ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。spfa 最坏情况下复杂度和朴素 bellman-ford 相同,为 O(nm)。 spfa算法通俗介绍(对比于dijks

idiot5lie的博客 532

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

算法设计与分析 作业二 答案与解析

第二次算法设计作业与解析 仅供参考

2301_80226956的博客 503

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

bellman -ford算法的改进 spfa 算法

转自:https://blog.csdn.net/xunalove/article/details/70045815求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。      SPFA算法是西南交通大学段凡丁于1994年发表的。    从名字我们就可以看出,这种算法在效率上一定有过人之处。      很多时候,给定的图存在负权边,这时类似Dij...

小灰狼与大白兔的博客 434

最短路(SPFA、Dijkstra、Floyd、Bellman-Ford)

SPFA: 求单源,时间复杂度O(kE)。(k 定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。 int spfa_dfs(int u) { vis[u]=1; for(int k=f[u]; k!=0; k=e[k].next) { int v=e[k].v,w=e[k].w; if( d[u]+w < d[v] )

梦醒了 911

Bellman-ford算法

一、Bellman-ford算法简介 Bellman-ford算法是一种求解“单源最短路径”算法,单源最短路就是求一个源点到其他结点的最短路径。而Bellman-ford算法最核心的思想就是“松弛操作”。我们用下图来具体操作一下Bellman-ford。 二、Bellman-ford算法详细解释 1.算法具体操作 首先,我们定义一个dis[ i ]数组, dis数组的意义是源点到其他点的经过边数不超过k(松弛操作次数)的最短距离。 假定 1号顶点是源点...

m0_58151858的博客 6518

Bellman-Ford算法

Bellman-Ford算法大致可以分成三部分: 1.初始化所有d[s],源点d[s]=0,其他d[s]=INF 2.进行n-1次循环,在循环体中遍历所有的边,进行松弛计算(if(d[v]>d[u]+w[u][v]) d[v]=d[u]+w[u][v]) 3.遍历图中所有的边,检验是否出现这种情况:d[v]>d[u]+w[u][v],若出现则返回false,没有最短路 Bel...

策马奔腾向前冲 1万+

【算法】算法学习五:加权图 | 狄克斯特拉算法

【算法】算法学习五:加权图 | 狄克斯特拉算法

wzk4869的博客 3550

图解 Dijkstra、Floyd、BellMan-Ford 最短路径算法

图解 Dijkstra Floyd BellMan-Ford 最短路径算法

weixin_54383080的博客 5720

【智能算法】Bellman-Ford算法

Bellman-Ford算法是一种用于解决单源最短路径问题的算法,特别适用于图中存在负权边的情况。该算法由理查德·贝尔曼(Richard Bellman)和莱斯特·福特(Lester Ford)共同创立,并因此得名。

大雨淅淅的博客 2357

bellman-ford算法详解

本文主要介绍了bellman-ford算法,详细易懂

gaoqiandr的博客 2237

最短路径: Dijkstra算法、Bellman-Ford算法、Floyd算法

目录 最短路径(Shortest Path) 最短路径 – 无权图 最短路径 – 负权边 最短路径 – 负权环 最短路径算法 Dijkstra (迪杰斯特拉算法) Dijkstra –思路 Dijkstra – 执行过程 Dijkstra –代码实现 Bellman-Ford (贝尔曼-福特算法) Bellman-Ford–思路 Bellman-Ford–思考 Bellman-Ford –执行过程 Bellman-Ford –代码实现 Floyd (弗洛伊德算法...

AN_ERA的博客 2525

算法——图之加权图

加权图这里指无向加权图。 加权图是一种为每条边关联一个权值或成本的图模型。也就是在无向图的基础上,每条边加上权值。 加权图有很多应用,例如航空图中,边表示航线,权值表示距离或是费用。还能表示电路图,边表示导线,权值表示导线长度或是成本等等。 在这些情形下,我们最感兴趣的当然是成本最小化,也就是最小生成树问题。 最小生成树 一副加权无向图的最小生成树(MST)是一棵权值之和最小的生

a60782885的博客 1万+
上一篇: 单源加权图最短路径问题(权值非负)-Dijkstra算法
下一篇: 找最近点对问题-分治算法的应用
midgard
博客等级 码龄21年 88粉丝 21原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值