程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 單源最短路徑Bellman-Ford算法

單源最短路徑Bellman-Ford算法

編輯:C++入門知識

對一個帶權有向圖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) 
            break; 
    } 
    return i; 

 
void MSWBellmanFord::BellmanInitSingleSource() 

    int vernum = bfordGraph->vertexnum; 
    for(int i = 0;i < vernum;i++) 
    { 
        swayweight[i] = MAXVALUE; 
        parent[i] = i; 
    } 

 
bool MSWBellmanFord::BellmanMSW(char sourceKey) 

    int location = BellmanFindKey(sourceKey); 
    int vernum = bfordGraph->vertexnum; 
    int i,j; 
    Edge *temp; 
    BellmanInitSingleSource(); 
    //swayweight[0] = 0;                                    //這裡為了偷懶,沒有隨意指定source,location本來是代表source的下標的  
    swayweight[location] = 0; 
    for(i = 0;i < vernum; i++) 
    { 
        /*
        for(j = 0;j < vernum; j++)
        {
            temp = bfordGraph->vertexs[j].adj;
            while(temp != NULL)
            {
                if((temp->weight + swayweight[j]) < swayweight[temp->verno])
                {
                    swayweight[temp->verno] = temp->weight + swayweight[j];
                    parent[temp->verno] = j;
                }
                temp = temp->next;
            }
        }
        */ 
        temp = bfordGraph->vertexs[location].adj; 
        while(temp != NULL) 
        { 
            if((temp->weight + swayweight[location]) < swayweight[temp->verno]) 
            { 
                swayweight[temp->verno] = temp->weight + swayweight[location]; 
                parent[temp->verno] = location; 
            } 
            temp = temp->next; 
        } 
        j = (location + 1) % vernum; 
        while(j != location) 
        { 
            temp = bfordGraph->vertexs[j].adj; 
            while(temp != NULL) 
            { 
                if((temp->weight + swayweight[j]) < swayweight[temp->verno]) 
                { 
                    swayweight[temp->verno] = temp->weight + swayweight[j]; 
                    parent[temp->verno] = j; 
                } 
                temp = temp->next; 
            } 
            j = (j + 1) % vernum; 
        } 
         
    } 
 
    for(j = 0;j < vernum; j++) 
    { 
        temp = bfordGraph->vertexs[j].adj; 
        while(temp != NULL) 
        { 
            if((temp->weight + swayweight[j]) < swayweight[temp->verno]) 
            { 
                return false; 
            } 
            temp = temp->next; 
        } 
    } 
    return true; 

 
void MSWBellmanFord::BellmanOutput() 

    int i,j,weight; 
    list<int> route; 
    cout<<"All the most shortest route from source : "<<endl; 
    for(i = 0;i < bfordGraph->vertexnum;i++) 
    { 
        j = i; 
        weight = swayweight[j]; 
        do 
        { 
            route.push_front(j); 
            j = parent[j]; 
 
        }while(parent[j] != j); 
 
        cout<<bfordGraph->vertexs[j].key; 
        cout<<"---"<<bfordGraph->vertexs[route.front()].key; 
        route.pop_front(); 
        while(!route.empty()) 
        { 
            if(route.front() != j) 
                cout<<"---"<<bfordGraph->vertexs[route.front()].key; 
            route.pop_front(); 
        } 
        cout<<"    "<<weight<<endl; 
    } 
 

 
int main() 

    char vertex[] = {'S','T','X','Y','Z'}; 
    int vernum = 5; 
    char adj[][2] = {{'S','T'},{'S','Y'},{'T','X'},{'T','Y'},{'T','Z'},{'X','T'},{'Y','X'},{'Y','Z'},{'Z','S'},{'Z','X'}}; 
    int weight[] = {6,7,5,8,-4,-2,-3,9,2,7}; 
    int adjnum = 10; 
    MSWBellmanFord *bellford = new MSWBellmanFord(vertex,vernum,adj,weight,adjnum); 
    bellford->BellmanMSW('S'); 
    bellford->BellmanOutput(); 
 
    return 0; 

#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)
   break;
 }
 return i;
}

void MSWBellmanFord::BellmanInitSingleSource()
{
 int vernum = bfordGraph->vertexnum;
 for(int i = 0;i < vernum;i++)
 {
  swayweight[i] = MAXVALUE;
  parent[i] = i;
 }
}

bool MSWBellmanFord::BellmanMSW(char sourceKey)
{
 int location = BellmanFindKey(sourceKey);
 int vernum = bfordGraph->vertexnum;
 int i,j;
 Edge *temp;
 BellmanInitSingleSource();
 //swayweight[0] = 0;         //這裡為了偷懶,沒有隨意指定source,location本來是代表source的下標的
 swayweight[location] = 0;
 for(i = 0;i < vernum; i++)
 {
  /*
  for(j = 0;j < vernum; j++)
  {
   temp = bfordGraph->vertexs[j].adj;
   while(temp != NULL)
   {
    if((temp->weight + swayweight[j]) < swayweight[temp->verno])
    {
     swayweight[temp->verno] = temp->weight + swayweight[j];
     parent[temp->verno] = j;
    }
    temp = temp->next;
   }
  }
  */
  temp = bfordGraph->vertexs[location].adj;
  while(temp != NULL)
  {
   if((temp->weight + swayweight[location]) < swayweight[temp->verno])
   {
    swayweight[temp->verno] = temp->weight + swayweight[location];
    parent[temp->verno] = location;
   }
   temp = temp->next;
  }
  j = (location + 1) % vernum;
  while(j != location)
  {
   temp = bfordGraph->vertexs[j].adj;
   while(temp != NULL)
   {
    if((temp->weight + swayweight[j]) < swayweight[temp->verno])
    {
     swayweight[temp->verno] = temp->weight + swayweight[j];
     parent[temp->verno] = j;
    }
    temp = temp->next;
   }
   j = (j + 1) % vernum;
  }
  
 }

 for(j = 0;j < vernum; j++)
 {
  temp = bfordGraph->vertexs[j].adj;
  while(temp != NULL)
  {
   if((temp->weight + swayweight[j]) < swayweight[temp->verno])
   {
    return false;
   }
   temp = temp->next;
  }
 }
 return true;
}

void MSWBellmanFord::BellmanOutput()
{
 int i,j,weight;
 list<int> route;
 cout<<"All the most shortest route from source : "<<endl;
 for(i = 0;i < bfordGraph->vertexnum;i++)
 {
  j = i;
  weight = swayweight[j];
  do
  {
   route.push_front(j);
   j = parent[j];

  }while(parent[j] != j);

  cout<<bfordGraph->vertexs[j].key;
  cout<<"---"<<bfordGraph->vertexs[route.front()].key;
  route.pop_front();
  while(!route.empty())
  {
   if(route.front() != j)
    cout<<"---"<<bfordGraph->vertexs[route.front()].key;
   route.pop_front();
  }
  cout<<"    "<<weight<<endl;
 }

}

int main()
{
 char vertex[] = {'S','T','X','Y','Z'};
 int vernum = 5;
 char adj[][2] = {{'S','T'},{'S','Y'},{'T','X'},{'T','Y'},{'T','Z'},{'X','T'},{'Y','X'},{'Y','Z'},{'Z','S'},{'Z','X'}};
 int weight[] = {6,7,5,8,-4,-2,-3,9,2,7};
 int adjnum = 10;
 MSWBellmanFord *bellford = new MSWBellmanFord(vertex,vernum,adj,weight,adjnum);
 bellford->BellmanMSW('S');
 bellford->BellmanOutput();

 return 0;
}
結果如下:


[cpp]
All the most shortest route from source : 
S---S    0 
S---Y---X---T    2 
S---Y---X    4 
S---Y    7 
S---Y---X---T---Z    -2 
請按任意鍵繼續. . . 

All the most shortest route from source :
S---S    0
S---Y---X---T    2
S---Y---X    4
S---Y    7
S---Y---X---T---Z    -2
請按任意鍵繼續. . .

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved