Bellman-Ford算法 C++

Bellman-Ford算法是一种解决最短路径问题的动态规划算法,该问题是求解从源节点到其他节点的最短路径。与Dijkstra算法不同的是,Bellman-Ford算法可以处理带有负权边的图。该算法的时间复杂度为O(V*E),其中V是节点的数量,E是边的数量。

Bellman-Ford算法的原理如下:
1. 初始化所有节点的距离为无穷大,源节点的距离为0。
2. 进行V-1次循环,每次循环遍历所有的边,对于每个边(u,v),如果通过u节点可以获得更短的距离到达v节点,则更新v节点的距离为新的更短距离。
3. 检测是否存在负权回路。再进行一次循环遍历所有的边,如果在这次循环中仍然存在节点的距离被更新,则说明存在负权回路。

使用Bellman-Ford算法可以解决以下问题:
1. 单源最短路径:给定一个源节点,求解从源节点到其他节点的最短路径和距离。
2. 检测负权回路:判断图中是否存在负权回路。

由于Bellman-Ford算法可以处理负权边,但是不能处理负权回路。如果存在负权回路,则算法会进入无限循环。因此,在实际应用中,需要注意检测负权回路的存在,可以通过增加一个计数器来限制循环次数,避免死循环的发生。

优点:
1. 适用范围广:Bellman-Ford算法适用于有向图和带负权边的情况,这使得它在实际应用中非常有用。
2. 可以处理负权边:相比于Dijkstra算法,Bellman-Ford算法可以处理负权边。这使得它在某些场景下更为适用。
3. 可以检测负权环:Bellman-Ford算法可以检测是否存在负权环。如果存在负权环,则说明图中不存在最短路径。

缺点:
1. 时间复杂度高:Bellman-Ford算法的时间复杂度为O(VE),其中V是顶点数,E是边数。这使得它在有大量顶点和边的图中效率较低。

Bellman-Ford算法的代码实现

#include 
#define ll long long
#define endl "\n"
#define KUI ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
struct jgt
{
    int go;
    int d;
};
const int con = 1e5 + 4;
const int inf = 2139062143;
int n, m, k;
bool vis[con];
int dis[con];
vector v[con];
bool BellmanFord()
{
    dis[1] = 0;  // 更新根节点距离;
    bool bj = 1; // 标记过程中是否发生更新节点的行为;
    for (int i = 1; i <= n; i++)
    {
        bj = 1;                      // 初始化标记;
        for (int j = 1; j <= n; j++) // n次遍历整个图;
        {
            if (dis[j] == inf) // 如果该节点距离仍为最大值,说明没有走到该节点,跳过该节点;
            {
                continue;
            }
            for (auto &x : v[j])
            {
                if (dis[x.go] > dis[j] + x.d)
                {
                    dis[x.go] = dis[j] + x.d;
                    bj = 0; // 如果发生了节点距离变化的过程,更新标记;
                }
            }
        }
        if (bj == 1) // 标记没有被更新,说明没有节点的距离发生变化,图的最短距离已经计算完成;
        {
            return bj;
        }
    }
    return bj;
    // 若图无负环,n次遍历必定可以计算完成最短距离,否则即为图中存在负环;
}
void take()
{
    cin >> n >> m;
    memset(dis, 127, sizeof dis); // 将dis赋值为较大数;
    int a1;
    struct jgt ans;
    for (int i = 1; i <= m; i++)
    {
        cin >> a1 >> ans.go >> ans.d;
        v[a1].push_back(ans); // 存图路径;
    }
    bool pd = BellmanFord();
    if (pd == 1) // 如果返回值为1,则代表最终BellmanFord函数没有再更新节点,说明图没有包含负环;
    {
        cout << "无环" << endl;
    }
    else
    {
        cout << "有环" << endl;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << dis[i] << endl;
    }
}
int main()
{
    KUI;
    int t1 = 1;
    // cin >> t1;
    while (t1--)
    {
        take();
    }
    return 0;
}

 利用队列优化Bellman-Ford算法:

因为只有改变过距离的节点,之后的节点的距离才会有变化,

所以,我们只需要队列保存距离改变过的点,从而更新距离发生改变的以后的点;

#include 
#define ll long long
#define endl "\n"
#define KUI ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
struct jgt
{
    int go;
    int d;
};
const int con = 1e5 + 4;
const int inf = 2139062143;
int n, m, k;
bool vis[con];
int dis[con], cont[con]; // cont[i]记录到达i节点经历的边;
vector v[con];
queue q;
bool BellmanFord()
{
    dis[1] = 0; // 更新根节点距离;
    vis[1] = 1;
    q.push(1); // 入队根节点,标记根节点;
    while (q.size() > 0)
    {
        int ans = q.front();
        vis[ans] = 0;
        q.pop(); // 节点出队,取消标记节点;
        for (auto &x : v[ans])
        {
            if (dis[x.go] > dis[ans] + x.d)
            {
                dis[x.go] = dis[ans] + x.d;
                if (vis[x.go] == 0) // 节点不在队中则入队,如果节点在队中则不需要入队;
                {                   // 如果节点已经在队中,那么距离总会更新;
                    q.push(x.go);
                    vis[x.go] = 1;
                }
                cont[x.go] = cont[ans] + 1;
                if (cont[x.go] >= n) // 如果到达该节点的边数大于等于节点数,说明有节点重复走过了,存在负环;
                {
                    return 0;
                }
            }
        }
    }
    return 1;
    // 若图无负环,n次遍历必定可以计算完成最短距离,否则即为图中存在负环;
}
void take()
{
    cin >> n >> m;
    memset(dis, 127, sizeof dis); // 将dis赋值为较大数;
    int a1;
    struct jgt ans;
    for (int i = 1; i <= m; i++)
    {
        cin >> a1 >> ans.go >> ans.d;
        v[a1].push_back(ans); // 存图路径;
    }
    bool pd = BellmanFord();
    if (pd == 1) // 如果返回值为1,则代表最终BellmanFord函数没有再更新节点,说明图没有包含负环;
    {
        cout << "无环" << endl;
    }
    else
    {
        cout << "有环" << endl;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << dis[i] << endl;
    }
}
int main()
{
    KUI;
    int t1 = 1;
    // cin >> t1;
    while (t1--)
    {
        take();
    }
    return 0;
}

你可能感兴趣的:(算法,数据结构,学习,c++,图论)