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;
}

 

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