zoukankan      html  css  js  c++  java
  • 求最短路径(Bellman-Ford算法与Dijkstra算法)

    前言

    Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。
    在网络路由中,RIP协议(距离向量路由算法)一般用Bellman-Ford算法,同时由于简单性所以也适用于分布式系统;但是它的复杂度是O(VE),比Dijkstra算法要慢上许多。而OSPF协议,链路状态分组创建的时候一般用Dijkstra算法,因为它的速度快。

    Bellman-Ford算法

    算法步骤
    1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0;
    2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
    3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dist[v]中。
    

    该算法是利用动态规划的思想,自底向上的方式计算最短路径。

    #include <iostream>
    using namespace std;
    const int maxnum = 100;
    const int maxint = 99999;
    
    // 边,
    typedef struct Edge {
    	int u, v;    // 起点,重点
    	int weight;  // 边的权值
    }Edge;
    
    Edge edge[maxnum];     // 保存边的值
    int  dist[maxnum];     // 结点到源点最小距离
    
    int nodenum, edgenum, source;    // 结点数,边数,源点
    
    								 // 初始化图
    void init()
    {
    	// 输入结点数,边数,源点
    	cin >> nodenum >> edgenum >> source;
    	for (int i = 1; i <= nodenum; ++i)
    		dist[i] = maxint;
    	dist[source] = 0;
    	for (int i = 1; i <= edgenum; ++i)
    	{
    		cin >> edge[i].u >> edge[i].v >> edge[i].weight;
    		if (edge[i].u == source)          //注意这里设置初始情况
    			dist[edge[i].v] = edge[i].weight;
    	}
    }
    
    // 松弛计算
    void relax(int u, int v, int weight)
    {
    	if (dist[v] > dist[u] + weight)
    		dist[v] = dist[u] + weight;
    }
    
    bool Bellman_Ford()
    {
    	for (int i = 1; i <= nodenum - 1; ++i)
    		for (int j = 1; j <= edgenum; ++j)
    			relax(edge[j].u, edge[j].v, edge[j].weight);
    	bool flag = 1;
    	// 判断是否有负环路
    	for (int i = 1; i <= edgenum; ++i)
    		if (dist[edge[i].v] > dist[edge[i].u] + edge[i].weight)
    		{
    			flag = 0;
    			break;
    		}
    	return flag;
    }
    int main()
    {
    	init();
    	if (Bellman_Ford())
    		for (int i = 1; i <= nodenum; i++)
    			cout << dist[i] << endl;
    	system("pause");
    	return 0;
    }
    

    Dijlstra算法

    1.初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为”起点s到该顶点的距离”[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
    2.从U中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从U中移除顶点k。
    3.更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。
    4.重复步骤(2)和(3),直到遍历完所有顶点。
    

    参照这篇博客可以对Dijlstra算法有一个清楚地理解。

    #include <iostream>
    using namespace std;
     
    const int maxnum = 100;
    const int maxint = 999999;
     
     
    void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
    {
        bool s[maxnum];    // 判断是否已存入该点到S集合中
        for(int i=1; i<=n; ++i)
        {
            dist[i] = c[v][i];
            s[i] = 0;     // 初始都未用过该点
            if(dist[i] == maxint)
                prev[i] = 0;
            else
                prev[i] = v;
        }
        dist[v] = 0;
        s[v] = 1;
     
        // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
        // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
        for(int i=2; i<=n; ++i)
        {
            int tmp = maxint;
            int u = v;
            // 找出当前未使用的点j的dist[j]最小值
            for(int j=1; j<=n; ++j)
                if((!s[j]) && dist[j]<tmp)
                {
                    u = j;              // u保存当前邻接点中距离最小的点的号码
                    tmp = dist[j];
                }
            s[u] = 1;    // 表示u点已存入S集合中
     
            // 更新dist
            for(int j=1; j<=n; ++j)
                if((!s[j]) && c[u][j]<maxint)
                {
                    int newdist = dist[u] + c[u][j];
                    if(newdist < dist[j])
                    {
                        dist[j] = newdist;
                        prev[j] = u;
                    }
                }
        }
    }
     
    void searchPath(int *prev,int v, int u)
    {
        int que[maxnum];
        int tot = 1;
        que[tot] = u;
        tot++;
        int tmp = prev[u];
        while(tmp != v)
        {
            que[tot] = tmp;
            tot++;
            tmp = prev[tmp];
        }
        que[tot] = v;
        for(int i=tot; i>=1; --i)
            if(i != 1)
                cout << que[i] << " -> ";
            else
                cout << que[i] << endl;
    }
     
    int main()
    {
        freopen("input.txt", "r", stdin);
        // 各数组都从下标1开始
        int dist[maxnum];     // 表示当前点到源点的最短路径长度
        int prev[maxnum];     // 记录当前点的前一个结点
        int c[maxnum][maxnum];   // 记录图的两点间路径长度
        int n, line;             // 图的结点数和路径数
     
        // 输入结点数
        cin >> n;
        // 输入路径数
        cin >> line;
        int p, q, len;          // 输入p, q两点及其路径长度
     
        // 初始化c[][]为maxint
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
                c[i][j] = maxint;
     
        for(int i=1; i<=line; ++i)  
        {
            cin >> p >> q >> len;
            if(len < c[p][q])       // 有重边
            {
                c[p][q] = len;      // p指向q
                c[q][p] = len;      // q指向p,这样表示无向图
            }
        }
     
        for(int i=1; i<=n; ++i)
            dist[i] = maxint;
        for(int i=1; i<=n; ++i)
        {
            for(int j=1; j<=n; ++j)
                printf("%8d", c[i][j]);
            printf("
    ");
        }
     
        Dijkstra(n, 1, dist, prev, c);
     
        // 最短路径长度
        cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;
     
        // 路径
        cout << "源点到最后一个顶点的路径为: ";
        searchPath(prev, 1, n);
    }
    
  • 相关阅读:
    Windows10关机问题----只有“睡眠”、“更新并重启”、“更新并关机”,但是又不想更新,解决办法
    3ds max学习笔记(九)-- 实例操作(路径阵列)
    3ds max学习笔记(八)-- 实例操作(直行楼梯)
    3ds max学习笔记(七)-- 实例操作(桌子)
    3ds max学习笔记(六)-- 基本操作(建模前奏)
    UE4入门(二)建立和打开项目
    3ds max学习笔记(五)--操作工具
    3ds max 学习笔记(四)--创建物体
    3ds max学习笔记(一)--选择物体
    欧拉回路输出(DFS,不用回溯!)Watchcow POJ 2230
  • 原文地址:https://www.cnblogs.com/yunlambert/p/9193765.html
Copyright © 2011-2022 走看看