当前位置:编程学习 > C/C++ >>

单源最短路径Bellman-Ford算法

对一个带权有向图G=(V,E),给定一个源顶点S,找出S到图中其他顶点v的最短路径即单源最短路径问题。该问题还有很多变体,像单终点最短路径、单对顶点最短路径、每对顶点间的最短路径等等。

最短路径问题是具有最优子结构的:一对顶点间的最短路径包含了该路径上的顶点间的最短路径。直观上理解,如果该路径上的两个顶点间的路径pij不是最短路径,那么用这两个顶点间的最短路径代替pij,那么就会出现一条更短的路径,与前面所说的最短路径矛盾。(具体证明参见算法导论P358)。

需要说明的是负权值边和松弛技术。Dijkstra算法是不允许图中存在负权边的,否则无法得到正确的结果。而Bellman-ford算法就允许图中存在负权边,而且该算法可以检测图中是否存在负权回路。两种算法都用到了松弛技术。即对边(u,v),如果通过u到达v比当前找到的到v的最短路径还短,那么就更新d[v]、parent[v]。通过松弛,可以减小最短路径估计。

Bellman-ford算法:

因为图中任意两个顶点的最短路径最多包含|V|-1条边,所以至多对每条边进行|V|-1次松弛后就会得到任意两个顶点间的实际最短路径。如果还能通过松弛降低最短路径估计,那么就可以断定图中存在负权回路,因为如果从s到v的路径中包含负权回路,那么s到v的最短路径长度就是负无穷了。可以这样理解,第i(i>=1)次松弛得到的是源点s到每个顶点vV的路径长度为i的最短路径,第|V|-1次松弛得到的就是长度为|V|-1的最短路径。不过,显然不是每个顶点到s的最短路径长度都是|V|-1,所以对每条边都进行|V|-1次松弛操作是没有必要的。Bellman-ford的时间复杂度为O(VE)。可以对该算法进行简单的优化,如果本次循环并未对任何一条边进行松弛,那么可以判定已经得到了最终结果,退出循环。

如图所示:

 \
 

 

 \
 

代码如下:


[cpp]
#include<iostream>  
#include<list>  
using namespace std; 
 
#define MAXVALUE 10000          //定义一个最长路径   
 
//此处Prim算法的图为有向图  
 
struct Edge 

    int verno;          //邻接数组中节点编号  
    int weight;         //权值  
    Edge* next;         //指向下一条边  
}; 
 
struct Vertex 

    Edge *adj;          //所指向的节点所在边  
    int verno;          //邻接数组中节点编号  
    char key;           //关键字  
}; 
 
struct Graph 

    Vertex *vertexs;    //节点数组  
    int vertexnum;      //节点个数  
    int adjnum;         //边数  
}; 
 
class MSWBellmanFord 

public: 
    MSWBellmanFord(char *vertex,int vernum,char adj[][2],int *weight,int adjnum); 
    void BellmanInsert(int source,int dest,int weight); 
    int BellmanFindKey(char key); 
    void BellmanInitSingleSource(); 
    bool BellmanMSW(char sourceKey); 
    void BellmanOutput(); 
private: 
    int *swayweight; 
    int *parent; 
    Graph *bfordGraph; 
}; 
 
MSWBellmanFord::MSWBellmanFord(char *vertex,int vernum,char adj[][2],int *weight,int adjnum) 

    int i,source,dest; 
 
    swayweight = new int[vernum]; 
    parent = new int[vernum]; 
    bfordGraph = new Graph; 
 
    bfordGraph->vertexs = new Vertex[vernum]; 
    bfordGraph->adjnum = adjnum; 
    bfordGraph->vertexnum = vernum; 
    for(i = 0;i < vernum;i++) 
    { 
        bfordGraph->vertexs[i].key = vertex[i]; 
        bfordGraph->vertexs[i].verno = i; 
        bfordGraph->vertexs[i].adj = NULL; 
    } 
 
    for(i = 0;i < adjnum;i++) 
    { 
        source = BellmanFindKey(adj[i][0]); 
        dest = BellmanFindKey(adj[i][1]); 
        BellmanInsert(source,dest,weight[i]); 
        //BellmanInsert(dest,source,weight[i]);         //无向图与有向图的区别在此  
    } 

 
void MSWBellmanFord::BellmanInsert(int source,int dest,int weight) 

    if(bfordGraph->vertexs[source].adj == NULL || bfordGraph->vertexs[source].adj->weight > weight) 
    { 
        Edge* newnode = new Edge; 
        newnode->verno = dest; 
        newnode->weight = weight; 
        newnode->next = bfordGraph->vertexs[source].adj; 
        bfordGraph->vertexs[source].adj = newnode; 
    } 
    else 
    { 
        Edge* temp = bfordGraph->vertexs[source].adj; 
        while(temp->next != NULL)                        //插入新边的时候,把权值从低到高进行排序  
        { 
            if(temp->next->weight > weight) 
                break; 
            temp = temp->next; 
        } 
        Edge* newnode = new Edge; 
        newnode->verno = dest; 
        newnode->weight = weight; 
        newnode->next = temp->next; 
        temp->next = newnode; 
    } 

 
int MSWBellmanFord::BellmanFindKey(char key) 

    int i; 
    for(i = 0;i < bfordGraph->vertexnum;i++) 
    { 
        if(bfordGraph->vertexs[i].key == key) 
         &nbs

补充:软件开发 , C++ ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,