Bellman-Ford單源最短路徑實現

幾點說明:

1、實現參考了《算法導論》中的單源最短路徑的章節中的僞代碼。嚴格證明、以及深入學習請參考書籍。

2、Bellman-Ford最短路徑算法支持權值爲負值,且可以檢測是否存在權重爲負值的環路。

3、相比Bellman-Ford,還有其它適用於不同變體的單源最短路徑算法,譬如迪傑斯特拉(Dijkstra)最短路徑算法,後者適用於權值爲非負數的圖,且時間複雜度更低。同樣深入瞭解請參考《算法導論》。

3、簡單算法說明和輸入,見下圖

#include <math.h>
#include <iostream>

void relax(double *dist, double ** weight, int *prev, int u, int v)
{
    if (weight[u][v] < 0 || dist[u] < 0)
    {
        return;
    }

    if (dist[v] > dist[u] + weight[u][v])
    {
        dist[v] = dist[u] + weight[u][v];
        prev[v] = u;
    }
}

/**
 * @brief bellman_ford單源最短路徑
 * @param N         [IN]  頂點的個數
 * @param s         [IN]  源點的索引
 * @param weight    [IN]  有向圖權值表;如果爲無向圖,則雙向邊的權值對稱即可
 * @param prev      [OUT] 輸出最短路徑,每個節點的前一個節點的索引;輸入空指針即可,結果需要外部使用delete[]釋放堆內存
 * @return  是否存在權重爲負值的環路
 */
bool bellman_ford(int N, int s, double ** weight, int *&prev)
{
    prev = new int[N];
    double *dist = new double[N];   //最短距離值,根據需要可以作爲輸入輸出參數
    for (int i = 0; i < N; i++)
    {
        dist[i] = HUGE_VALD;    //初始化爲無窮大
        prev[i] = -1;			//初始化爲無效索引值
    }
    dist[s] = 0;    //從節點s找到其他節點的最短路徑

    for (int T = 0; T < N - 1; ++T)
    {
        for (int u = 0; u < N; u++)
        {
            for (int v = 0; v < N; v++)
            {
                if (u == v)
                {
                    continue;
                }

                if (weight[u][v] > 0)
                {
                    relax(dist, weight, prev, u, v);
                }
            }
        }
    }

    //判斷是否存在權重爲負值的環路
    for (int u = 0; u < N; u++)
    {
        for (int v = 0; v < N; v++)
        {
            if (u == v)
            {
                continue;
            }

            if (weight[u][v] > 0)
            {
                if (dist[v] > dist[u] + weight[u][v])
                {
                    return false;
                }
            }
        }
    }

    return true;
}

int main()
{
    //1、構造5*5輸入
    const double INF = 999999;  //HUGE_VALD、DBL_MAX
    double weight0[5][5] = {
        INF, 10, INF,  5, INF,
        INF, INF,  1,  2, INF,
        INF, INF, INF, INF,  4,
        INF,  3, 9,  INF,  2,
        7, INF,  6, INF, INF
    };

    int N = 5;
    int *prev = NULL;
    double **weight = new double*[N];
    for (int i = 0; i < N; i++)
    {
        weight[i] = new double[N];
        memcpy(weight[i], weight0[i], sizeof(double)*5);
    }

    //2、計算
    bellman_ford(5, 0, weight, prev);

    //3、輸出結果(每個節點的前一個節點的索引)
    for (int i = 0; i < 5; i++)
    {
        std::cout << i << ".prev=" << prev[i] << std::endl;
    }
    //    0.prev=-1
    //    1.prev=3
    //    2.prev=1
    //    3.prev=0
    //    4.prev=3


    //4、釋放堆內存
    for (int i = 0; i < N; i++)
    {
        delete []weight[i];
    }
    delete []weight;
    delete []prev;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章