zoukankan      html  css  js  c++  java
  • Algorithm --> Dijkstra和Floyd最短路径算法

    Dijkstra算法

    一.最短路径的最优子结构性质

       该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

       假设P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而 P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P'(k,s),那么 P'(i,j)=P(i,k)+P'(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。

    二.Dijkstra算法

       由上述性质可知,如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点。那么(Vi...Vk)也必定是从i到k的最短 路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最 短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根 据这种思路,

    假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。

    1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;

    2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})

    3.知道U=V,停止。

    三.算法实例

    先给出一个无向图

    用Dijkstra算法找出以A为起点的单源最短路径步骤如下

    代码:

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    #define DEBUG 0
    #define INF 0x7fffffff
    #define MAX 20
    
    int N, V;
    int graph[MAX][MAX];
    int dist[MAX];
    int prev[MAX];
    bool visited[MAX];
    
    void PrintPath()
    {
        for( int i = 1; i <= V; i++ )
        {
            cout << "1 --> "  << i << "  :  " << dist[i] << endl;
        }
    
        for( int i = 1; i <= V; i++ )    //计算从1到每个顶点的距离
        {
            int path[MAX] = {0};
            int step = 0;
            int cur = i;
            do
            {
                path[step++] = cur;
                cur = prev[cur];
            }
            while( cur != -1 );     //前一个点为-1,则结束
    
            for( int j = step - 1; j >= 0; j-- )
            {
                cout << path[j] << "  ";
            }
            cout << endl;
        }
    }
    
    int GetMinDist()
    {
        int index, min = INF;
        for( int i = 1; i <= V; i++ )
        {
            if( !visited[i] && min > dist[i] )
            {
                min = dist[i];
                index = i;
            }
        }
        return index;
    }
    
    void Dijkstra( int v )
    {
        for( int i = 1; i <= V; i++ )
        {
            if( graph[v][i] == INF )
            {
                dist[i] = INF;
                prev[i] = -1;
            }
            else
            {
                dist[i] = graph[v][i];
                prev[i] = v;
            }
            visited[i] = false;
        }
    
        dist[v] = 0;
        visited[v] = true;
    
        for( int i = 1; i < V; i++ ) //迭代V-1次,不用计算源点了,还剩下V-1个需要计算的顶点
        {
            int u = GetMinDist();
    
            visited[u] = true;
    
            for( int j = 1; j <= V; j++ )  //更新dist数组
            {
                if( visited[j] == false && graph[u][j] != INF && dist[u] != INF && dist[j] > dist[u] + graph[u][j] )
                {
                    dist[j] = dist[u] + graph[u][j];
                    prev[j] = u;
                }
            }
        }
    }
    
    void InitData()
    {
        memset( visited, false, sizeof( visited ) ); //初始化visited
    
        for( int i = 0; i <= V; i++ )
        {
            for( int j = 0; j <= V; j++ )
            {
                graph[i][j] = INF;
            }
            dist[i] = INF;
        }
    }
    
    int main()
    {
        int a, b, value;
        while( cin >> V, V ) // 输入顶点数
        {
            cin >> N;         //输入边数
            InitData();
            for( int i = 1; i <= N; i++ )
            {
                cin >> a >> b >> value;
                graph[a][b] = graph[b][a] = value;
            }
    
            Dijkstra( 1 );
            PrintPath();
        }
    }

    输入文件:

    /*
    
    6 10
    1 2 4
    1 3 8
    2 3 3
    2 4 4
    2 5 6
    3 4 2
    3 5 2
    4 5 4
    4 6 9
    5 6 4
    
    result :
    1 --> 1  :  0
    1 --> 2  :  4
    1 --> 3  :  7
    1 --> 4  :  8
    1 --> 5  :  9
    1 --> 6  :  13
    1
    1 2
    1 2 3
    1 2 4
    1 2 3 5
    1 2 3 5 6
    
    */

    Floyd算法

    1.算法思想原理:

         Floyd算法是一个经典的动态规划算法。从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,则设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

    2.算法描述:

    a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。   

    b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。

    3.Floyd算法过程矩阵的计算----十字交叉法

    方法:两条线,从左上角开始计算一直到右下角 如下所示

    给出矩阵,其中矩阵A是邻接矩阵,而矩阵Path记录u,v两点之间最短路径所必须经过的点

    相应计算方法如下:

    最后A3即为所求结果.

    算法实现:

    #include <iostream>
    
    using namespace std;
    
    #define INF 65536
    #define MAX 20
    
    int graph[MAX][MAX];
    int KeyPoint[MAX][MAX];
    int V, E;
    
    void PrintPath()
    {
        cout << graph[1][V] << endl;
        
        int path[MAX];
        int step = 0;
        int cur = V;
     
        while(cur != 0) {
            path[step++] = cur;
            cur = KeyPoint[1][cur]; 
        }
        
        path[step++] = 1;  //保存起点 
        
        for (int j = step - 1; j >= 0; j--)
        {
            cout << path[j] << "  ";
        }
        cout << endl;
    }
    
    void Floyd()
    {
        graph[1][1] = 0;
        
        for(int k = 1; k <= V; k++)   //对于每一个节点k,检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立
            for(int i = 1; i <= V; i++)
                for(int j = 1; j <= V; j++)
                    if(graph[i][j] > graph[i][k] + graph[k][j])
                    {
                        graph[i][j] = graph[i][k] + graph[k][j];
                        KeyPoint[i][j] = k;
                    }
        PrintPath();
    }
    
    void InitData()
    {
        for(int i = 1; i <= V; i++)
        {
            for(int j = 1; j <= V; j++)
            {
                graph[i][j] = INF;
                KeyPoint[i][j] = 0;
            }  
        }
    }
    
    int main()
    {
        int a, b, length;
        while(cin >> V, V)  //输入顶点数 
        {
            InitData();
            
            cin >> E;    //输入边数 
            for(int i = 1; i <= E; i++)
            {
                cin >> a >> b >> length;
                graph[a][b] = graph[b][a] = length;
            }
            
            Floyd();
        }
    } 

    测试用例

    /*
    
    6 10
    1 2 4
    1 3 8
    2 3 3
    2 4 4
    2 5 6
    3 4 2
    3 5 2
    4 5 4
    4 6 9
    5 6 4
    
    result:
    13
    1  2  3  5  6
    
    */
  • 相关阅读:
    Maven仓库详解
    Maven镜像配置
    使用spring的jdbcTemplate-----用JDBC模板查询数据库
    struts2+spring的两种整合方式
    Spring 中设置依赖注入
    Struts_json插件配置参数
    String、StringBuffer与StringBuilder之间区别
    有关collection中的一些数据结构
    MyBatis的foreach语句详解
    struts文件上传拦截器中参数的配置(maximumSize,allowedTypes ,allowedExtensions)问题
  • 原文地址:https://www.cnblogs.com/jeakeven/p/4634397.html
Copyright © 2011-2022 走看看