《啊哈!算法》之最短路徑(Dijkstra算法和bellman-ford算法及其隊列優化)

最短路徑

  • Dijkstra
  • Bellman-Ford

Dijkstra

該算法的基本思想爲:
每次找到離源點最近的一個頂點,然後以該頂點爲中心進行擴展最終得到源點到其餘所有點的最短路徑。

基本步驟如下:

  1. 將所有頂點分爲兩部分:已知最短路程的頂點集合P和未知最短路程的頂點集合Q。用book[i]表示,如果book[i]=1則表示這個頂點在集合P中,反之頂點在集合Q中。
  2. 設置源點s到自己的最短路徑爲0。其餘按照實際情況進行設置。
  3. 在集合Q的所有定點中選擇一個離源點s最近的頂點加入到集合P。並考察所有以點u爲起點的邊,對每一條邊進行鬆弛操作。
  4. 重複第三步,如果集合Q爲空,算法結束。最終dis數組中的值就是源點到所有頂點的最短路徑。
#include<iostream>
using namespace std;
#define MAX 999999
int main ()
{
    int book[101],e[101][101],dis[10];    //book[i]=0表示在未確定最小路徑的集合中,book[i]=1表示在確定最小路徑的集合中
    int s,n,m;
    int a,b,c;
    int i,j,u;
    cout << "please input the number of the  city: ";
    cin >> n;
    cout << "is there how many roads: ";
    cin >> m;
    cout << "please input the start: ";
    cin >> s;
    for(int i =1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            if(i == j)
            {
                e[i][j]=0;
            }
            else
                e[i][j]=MAX;
        }
    }
    cout << "please input the information of the roads: ";
    for(int k=1;k<=m;++k)
    {
         cin >> a >> b >> c;
         e[a][b]=c;
    }
    for(i=1;i<=n;++i)
        dis[i]=e[s][i];
    for(i=1;i<=n;++i)
        book[i]=0;
    book[s]=1;
    for(int k=1;k<=n-1;++k)                //要去找n-1次,因爲最多包含n-1次鬆弛    //算法的核心代碼
    {
        for(j=1;j<=n;++j)
        {
            int minute=MAX;
            if((book[j] ==0) && (dis[j]< minute))    //確定相鄰城市的中距離最小的是哪個
            {
                minute = dis[j];
                u=j;
            }
        }
        book[u]=1;
        for(i=1;i<=n;++i)
        {
            if(e[u][i]<MAX)                
            {
                if(dis[i]>(dis[u]+e[u][i]))    //將路徑鬆弛
                    dis[i]=dis[u]+e[u][i];
            }
        }
    }
    for(i=1;i<=n;++i)
        cout << dis[i]<<" ";
    return 0;
}

Bellman-Ford及其隊列優化

#include<iostream>
using namespace std;
#define MAX 999999

int main()
{
    int u[101],v[101],w[101],dis[10];
    int s,n,m;
    int k,i;
    cout << "please input the number of the  city: ";
    cin >> n;
    cout << "is there how many roads: ";
    cin >> m;
    cout << "please input the start: ";
    cin >> s;
    cout << "please input the information of the roads: ";
    for(k=1;k<=m;++k)
    {
        cin >> u[k] >> v[k] >> w[k];
    }
    for(i=1;i<=n;++i)
        dis[i]=MAX;
    dis[s]=0;
    for(k=1;k<=n-1;++k)    //進行n-1輪鬆弛
    {
        for(i=1;i<=n;++i)   //枚舉每一條邊
        {
            if(dis[v[i]]>(dis[u[i]]+w[i]))    //對每一條邊進行鬆弛
                dis[v[i]]=dis[u[i]]+w[i];
                //dis[1][v[i]] = dis[u[i]] + w[i];
        }
    }
    for(i=1;i<=n;++i)
        cout << dis[i] << " ";
    return 0;
}

上述代碼中,外循環一共循環了n-1次(n爲頂點的個數),內循環循環了m次(m爲邊的個數),即枚舉每一條邊。dis數組的作用與Dijkstra算法一樣,是用來記錄源點到其餘各個頂點的最短路徑。u、v合w三個數組是用來記錄邊的信息。

隊列優化

隊列優化最核心的思想就是:
每次僅對最短路徑估計值發生了變化的頂點的所有出邊執行鬆弛操作。

每次選取隊首頂點u,對頂點的所有出邊進行鬆弛操作。例如有一條u→v的邊,如果通過u→v這條邊是的源點到頂點v的最短路程變短,且頂點v不在當前的隊列中,就將頂點v放入隊尾。需要注意的是,同一個頂點同時在隊列中出現多次是毫無意義的,所以我們需要一個數組來判重。在對頂點u的所有出邊鬆弛完畢後,就將頂點u出隊。

#include<iostream>
using namespace std;
#define INF 999999

int main ()
{
    int dis[6],que[101]={0},book[6];
    int first[10],next[10];
    int u[8],v[8],w[8];
    int head=1,tail=1;
    int s,n,m,i;
    cout << "please input the number of the  city: ";
    cin >> n;
    cout << "is there how many roads: ";
    cin >> m;
    cout << "please input the start: ";
    cin >> s;
    cout << "please input the information of the roads: ";
    for(i=1;i<=n;++i)
        dis[i]=INF;
    dis[s]=0;
    for(i=1;i<=n;++i)
        book[i]=0;
    for(i=1;i<=n;++i)
        first[i]=-1;
    for(i=1;i<=m;++i)
    {
        cin >> u[i] >> v[i] >> w[i];
        next[i]=first[u[i]];
        first[u[i]]=i;
    }
    que[tail]=s;
    ++tail;
    book[s]=1;
    while(head < tail)
    {
        int k;
        k=first[que[head]];
        while(k != -1)
        {
            if(dis[v[k]]>(dis[u[k]]+w[k]))
            {
                dis[v[k]]=dis[u[k]]+w[k];
                if(book[v[k]] == 0)
                {
                    que[tail]=v[k];
                    ++tail;
                    book[v[k]]=1;
                }
            }
            k=next[k];
        }
        book[que[head]]=0;
        ++head;
    }
    for(i=1;i<=n;++i)
        cout << dis[i] << " ";
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章