Bellman-Ford 算法

Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。

1、Bellman-Ford算法的流程如下:

①数组dist[i]记录从源点s到顶点i的路径长度,初始化数组dist[n]为INF, dist[s]为0;

②以下操作循环执行至多n-1次,n为顶点数:
 对于每一条边e(u, v),如果dist[u] + w(u, v) < dist[v],则另dist[v] = dist[u]+w(u, v)。w(u, v)为边e(u,v)的 权值;

③若上述操作没有对dist进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;

④为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在dist[u] + w(u, v) < dist[v]的边,则图中存在负环路,即是说该图无法求出单源最短路径。否则数组dist[n]中记录的就是源点s到各顶点的最短路径长度。

2、参考代码

/**
该代码修改自《挑战程序设计》第二版
*/
#include <iostream>
#include <string.h>
#include <stack>
#define INF 1000000000
#define MAX_E 99
#define MAX_V 100
using namespace std;
struct edge{
int  from,to,cost;//边的起点,终点,权值
};
edge es[MAX_E];
int d[MAX_V];//源点到顶点的最短距离
int path[MAX_V];//存最短路径
int V,E;//顶点数,边数
int s;//源点
void BellmanFord(int s);
bool find_negative_loop();//判断是否有负环
void Print();
int main()
{
    cout << "请输入图的顶点数,边数,源点:";
    cin >>V>>E>>s;

    cout << "请输入" <<E<< "条边的起点、终点以及权值:\n";
    for (int i = 0; i <E; i++)
        cin >>es[i].from>> es[i].to >> es[i].cost;
    if(!find_negative_loop()){
        BellmanFord(s);
        Print();
    }else{
    cout<<"有负环"<<endl;
    }

    return 0;
}
void BellmanFord(int s){
    for(int i=0;i<V;i++){
        d[i]=INF;
    }
    d[s]=0;
    while(true){//最多循环V-1次
        bool update=false;
        for(int i=0;i<E;i++){
            edge e=es[i];
            if(d[e.from]!=INF&&d[e.to]>d[e.from]+e.cost){
                d[e.to]=d[e.from]+e.cost;
                path[e.to]=e.from;
                update=true;
            }
        }
        if(!update)//如果没有边的距离更新说明已经找完了
            break;
    }

}
bool find_negative_loop(){
    memset(d,0,sizeof(d));
    for(int i=0;i<V;i++){
        for(int j=0;j<E;j++){
            edge e=es[j];
        if(d[e.to]>d[e.from]+e.cost){
            d[e.to]=d[e.from]+e.cost;
            if(i==  V-1)return true;//如果其在第V次仍然更新则说明其有负环
            ///因为最短路径不可能经过同一个点两次,所以最多循环V-1次
            ///但如果有负环会无限循环下去,自己可以举个栗子试一下
        }
        }
    }
    return false;

}

void Print()
{
    for (int i = 0; i < V; i++)
    {
        if (i != s)
        {
            int p = i;
            stack<int> st;
            cout << "顶点 " << s << " 到顶点 " << p << " 的最短路径是: ";

            while (s != p)  //路径顺序是逆向的,所以先保存到栈
            {
                st.push(p);
                p = path[p];
            }

            cout << s;
            while (!st.empty())  //依次从栈中取出的才是正序路径
            {
                cout << "--" << st.top();
                st.pop();
            }
            cout << "    最短路径长度是:" << d[i] << endl;
        }

    }
}

 

posted @ 2017-08-06 14:39  路人姜。  阅读(228)  评论(0编辑  收藏  举报