單源最短路徑算法--Dijkstra算法和Bellman-Ford算法


Dijkstra算法

算法流程:
(a) 初始化:用起點v到該頂點w的直接邊(弧)初始化最短路徑,否則設為∞;
(b) 從未求得最短路徑的終點中選擇路徑長度最小的終點u:即求得v到u的最短路徑;
(c) 修改最短路徑:計算u的鄰接點的最短路徑,若(v,…,u)+(u,w)<(v,…,w),則以(v,…,u,w)代替。
(d) 重復(b)-(c),直到求得v到其余所有頂點的最短路徑。
特點:總是按照從小到大的順序求得最短路徑。

假設一共有N個節點,出發結點為s,需要一個一維數組prev[N]來記錄前一個節點序號,一個一維數組dist[N]來記錄從原點到當前節點最短路徑(初始值為s到Vi的邊的權值,沒有則為+∞),一個二維數組weights[N][N]來記錄各點之間邊的權重,按以上流程更新prev[N]和dist[N]。

參考代碼:

#include <iostream>   
#include <cstdlib>   
using namespace std;  
  
void Dijkstra(int n,int s,int *dist,int *prev,int w[][4])  
{  
    int maxint = 65535;  
    bool *visit = new bool[n];  
  
    for (int i = 0; i < n; i++)  
    {  
        dist[i] = w[s][i];  
        visit[i] = false;  
        if (dist[i] != maxint)  
        {  
            prev[i] = s;  
        }  
    }  
  
    dist[s] = 0;  
    visit[s] = true;  
    for (int i = 0; i < n; i++)  
    {  
        int temp = maxint;  
        int u = s;  
        for (int j = 0; j < n; j++)  
        {  
            if ((!visit[j]) && (dist[j] < temp))  
            {  
                u = j;  
                temp = dist[j];  
            }  
        }  
        visit[u] = true;  
        for (int j = 0; j < n; j++)  
        {  
            if (!visit[j])  
            {  
                int newdist = dist[u] + w[u][j];  
                if (newdist < dist[j])  
                {  
                    dist[j] = newdist;  
                    prev[j] = u;  
                }  
            }  
        }  
    }  
  
    delete []visit;  
}  
  
int main()  
{  
    int n,v,u;  
    int weight[4][4]={  
        0,2,65535,4,  
        2,0,3,65535,  
        65535,3,0,2,  
        4,65535,2,0  
        };  
    int q = 0;  
    int way[4];  
    int dist[4];  
    int prev[4];  
    int s = 1;  
    int d = 3;  
    Dijkstra(4, s, dist, prev, weight);  
    cout<<"The least distance from "<<s<<" to "<<d<<" is "<<dist[d]<<endl;  
    int w = d;  
    while (w != s)  
    {  
        way[q++] = prev[w];  
        w = prev[w];  
    }  
    cout<<"The path is ";  
    for (int j = q-1; j >= 0; j--)  
    {  
        cout<<way[j]<<" ->";  
    }  
    cout<<d<<endl;  
  
    return 0;  
}  

Bellman-Ford算法

Bellman-Ford算法能在更普遍的情況下(存在負權邊)解決單源點最短路徑問題。對於給定的帶權(有向或無向)圖 G=(V,E),其源點為s,加權函數 w 是邊集 E 的映射。對圖G運行Bellman-Ford算法的結果是一個布爾值,表明圖中是否存在着一個從源點s可達的負權回路。若不存在這樣的回路,算法將給出從源點s到圖G的任意頂點v的最短路徑d[v]。

Bellman-Ford算法流程分為三個階段:

(1)初始化:將除源點外的所有頂點的最短距離估計值 d[v] ←+∞, d[s] ←0;
(2)迭代求解:反復對邊集E中的每條邊進行松弛操作,使得頂點集V中的每個頂點v的最短距離估計值逐步逼近其最短距離;(運行|v|-1次)
(3)檢驗負權回路:判斷邊集E中的每一條邊的兩個端點是否收斂。如果存在未收斂的頂點,則算法返回false,表明問題無解;否則算法返回true,並且從源點可達的頂點v的最短距離保存在 d[v]中。

算法描述如下:

Bellman-Ford(G,w,s) :boolean   //圖G ,邊集 函數 w ,s為源點
1        for each vertex v ∈ V(G) do        //初始化 1階段
2            d[v] ←+∞
3        d[s] ←0;                            //1階段結束
4        for i=1 to |v|-1 do                  //2階段開始,雙重循環。
5           for each edge(u,v) ∈E(G) do    //邊集數組要用到,窮舉每條邊。
6              If d[v]> d[u]+ w(u,v) then     //松弛判斷
7                 d[v]=d[u]+w(u,v)            //松弛操作   2階段結束
8        for each edge(u,v) ∈E(G) do
9            If d[v]> d[u]+ w(u,v) then
10            Exit false
11    Exit true

適用條件和范圍:
  1.單源最短路徑(從源點s到其它所有頂點v);
  2.有向圖&無向圖(無向圖可以看作(u,v),(v,u)同屬於邊集E的有向圖);
  3.邊權可正可負(如有負權回路輸出錯誤提示);
  4.差分約束系統;

#include <stdio.h>   
#include <stdlib.h>   
  
/* Let INFINITY be an integer value not likely to be 
   confused with a real weight, even a negative one. */  
     
#define INFINITY ((1 << 14)-1)   
  
typedef struct   
{  
    int source;  
    int dest;  
    int weight;  
} Edge;  
  
void BellmanFord(Edge edges[], int edgecount, int nodecount, int source)  
{  
    int *distance =(int*) malloc(nodecount*sizeof(int));  
    int i, j;  
  
    for (i=0; i < nodecount; ++i)  
       distance[i] = INFINITY;  
    distance[source] = 0;  
  
    for (i=0; i < nodecount; ++i)   
    {  
       int nbChanges = 0;   
       for (j=0; j < edgecount; ++j)   
       {  
            if (distance[edges[j].source] != INFINITY)   
            {  
                int new_distance = distance[edges[j].source] + edges[j].weight;  
                if (new_distance < distance[edges[j].dest])   
                {  
                  distance[edges[j].dest] = new_distance;  
                  nbChanges++;   
                }   
            }  
        }  
         // if one iteration had no impact, further iterations will have no impact either   
        if (nbChanges == 0) break;   
    }  
  
    for (i=0; i < edgecount; ++i)   
    {  
        if (distance[edges[i].dest] > distance[edges[i].source] + edges[i].weight)   
        {  
            puts("Negative edge weight cycles detected!");  
            free(distance);  
            return;  
        }  
    }  
  
    for (i=0; i < nodecount; ++i)   
    {  
        printf("The shortest distance between nodes %d and %d is %d\n", source, i, distance[i]);  
    }  
  
    free(distance);  
    return;  
}  
  
int main(void)  
{  
    /* This test case should produce the distances 2, 4, 7, -2, and 0. */  
    Edge edges[10] = {{0,1, 5}, {0,2, 8}, {0,3, -4}, {1,0, -2},  
                      {2,1, -3}, {2,3, 9}, {3,1, 7}, {3,4, 2},  
                      {4,0, 6}, {4,2, 7}};  
    BellmanFord(edges, 10, 5, 4);  
    return 0;  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM