zoukankan      html  css  js  c++  java
  • DS实验题 Dijkstra算法

    参考:Dijkstra算法

    数据结构来到了图论这一章节,网络中的路由算法基本都和图论相关。于是在拿到DS的实验题的时候,决定看下久负盛名的Dijkstra算法。
    Dijkstra的经典应用是开放最短路径优先路由算法(OSPF)。

    题目:


    第一种实现的算法(错误):

    在看过相关的Dijkstra算法解析之后,决定自己尝试写个算法,不过很遗憾,考虑并不全面,理解也不是特别深刻。
    最后决定还是贴出来。

    算法思想:
    1.建立邻接表(后面用二维数组实现,相对来说轻松很多)
    2.维护三个数组:dist代表从源点到某个节点的最短路径长度;ins代表这个节点是否已经被查询过;pre记录最短路径。
    3.算法主体:
    -1.当前节点 = 源节点。
    -2.从当前节点出发,遍历邻接节点,找到离它最近的节点,且这个节点尚未遍历过,同时更新dist。
    -3.跳转到找到的最近节点,先前节点的ins置1,回到2。
    -4.当当前节点附近没有未遍历的节点时,break。

    算法代码如下:

    //
    //  main.cpp
    //  Gank_Dijkstra
    //
    //  Created by wasdns on 16/11/15.
    //  Copyright © 2016年 wasdns. All rights reserved.
    //
    
    
    #include <cstdio>
    #include <iostream>
    #include <string.h>
    #include <string>
    #include <stack>
    using namespace std;
    #define maxn 100000000;
    
    struct edge {
        edge *next;
        int num;
        int len;
    } eg[100000];
    
    struct head {
        edge *next;
        int num;
    } h[100000];
    
    void IniList(int n)
    {
        int i;
        
        for (i = 1; i <= n; i++)
        {
            h[i].next = NULL;
            h[i].num = i;
        }
    }
    
    void CreatList(int n, int m)
    {
        int i;
        int x, y, leng;
        
        for (i = 1; i <= n; i++)
        {
            h[i].next = NULL;
            h[i].num = i;
        }
        
        for (i = 0; i < m; i++)
        {
            cin >> x >> y >> leng;
            
            edge *p1, *p2;
            
            p1 = new edge;
            p1 -> next = NULL;
            p1 -> num = y;
            p1 -> len = leng;
            
            p2 = new edge;
            p2 -> next = NULL;
            p2 -> num = x;
            p2 -> len = leng;
            
            edge *p3, *p4;
            
            p3 = h[x].next;
            
            if (p3 == NULL) {
                h[x].next = p1;
            }
            
            else
            {
                while (p3 -> next != NULL) {
                    p3 = p3 -> next;
                }
                
                p3 -> next = p1;
            }
            
            p4 = h[y].next;
            
            if (p4 == NULL) {
                h[y].next = p2;
            }
            
            else
            {
                while (p4 -> next != NULL) {
                    p4 = p4 -> next;
                }
                
                p4 -> next = p2;
            }
        }
    }
    
    void PrintList(int n)
    {
        int i;
        edge *p;
        
        for (i = 1; i <= n; i++)
        {
            p = h[i].next;
            
            cout << "Node:" << h[i].num << endl;
            
            while (p != NULL) {
                cout << p -> num << " " << p -> len << " ";
                p = p -> next;
            }
            
            cout << endl;
        }
    }
    
    int pre[105];
    
    int dist[105];
    
    bool ins[105];
    
    void Initial(int b, int n)
    {
        int i;
        
        memset(pre, -1, sizeof(pre));
        
        for (i = 1; i <= n; i++)
        {
            dist[i] = 10000000;
            
            ins[i] = false;
        }
        
        dist[b] = 0;
        
        pre[b] = b;
    }
    
    int Dijkstra(int b, int n, int end)
    {
        Initial(b, n);
        
        int cnt = 0;
        int turn = b;
        
        while (cnt < n)
        {
            edge *p;
            p = h[turn].next;
            
            int minlen = maxn;
            int tnext = turn;
            
            while (p != NULL)
            {
                if (ins[p -> num]) {
                    p = p -> next;
                    
                    continue;
                }
                
                int t = dist[turn] + p -> len;
                
                if (t < dist[p -> num]) {
                    dist[p -> num] = t;
                    
                    pre[p -> num] = turn;
                }
                
                if (t < minlen) {
                    minlen = t;
                        
                    tnext = p -> num;
                }
                
                p = p -> next;
            }
            
            ins[turn] = true;
            
            cnt++;
            
            if (turn == tnext) {
                turn = b;
            }
            
            turn = tnext;
        }
        
        return dist[end];
    }
    
    
    int main()
    {
        int n, m, s, e;
        
        cin >> n >> m >> s >> e;
        
        CreatList(n, m);
        
        PrintList(n);
        
        cout << Dijkstra(s, n, e) << endl;
        
        return 0;
    }
    
    /*
     
     5 6 1 5
     1 2 5
     2 5 5
     1 3 3
     3 5 7
     1 4 1
     4 5 9
    
    
     6 9 1 6
     1 3 3
     1 2 6
     2 3 2
     2 4 5
     3 4 3
     3 5 4
     4 5 2
     4 6 3
     5 6 5
     
     5 6 1 5
     1 2 7
     1 3 1
     1 4 3
     2 5 1
     3 5 10
     4 5 9
     
    */
    

    算法缺陷:

    测试一下下面这个样例,

     5 6 1 5
     1 2 7
     1 3 1
     1 4 3
     2 5 1
     3 5 10
     4 5 9
    

    答案应该为8,但是结果为11。

    原因在于,当以上算法经过:1 -> 3 -> 5 -> 2 最终来到节点2的时候,节点2附近没有未遍历的节点了(节点2),跳出循环。

    第二种算法:

    //
    //  main.cpp
    //  Dijkstra
    //
    //  Created by wasdns on 16/11/16.
    //  Copyright © 2016年 wasdns. All rights reserved.
    //
    
    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    
    int DijGraph[105][105];               //节点i和j之间的距离:DijGraph[i][j]
    
    int pre[105];                         //最短路节点的前继节点(查询最短路)
    
    int dist[105];                        //源点到每个节点的最短路距离
    
    bool ins[105];                        //节点i是否位于已查询节点集合S
    
    /*
        初始化函数
     */
    
    void Initial(int n)
    {
        for (int i = 1; i <= n; i++)
        {
            ins[i] = false;
            
            pre[i] = -1;
            
            for (int j = 1; j <= n; j++) {
                
                if (i == j) DijGraph[i][j] = 0;
                
                else DijGraph[i][j] = 1000000;
            }
        }
    }
    
    int cnt = 1;                                    //记录到目的节点的条数
    int belen = 1000000;                            //到目的节点的最短路长度
    
    void Dijkstra(int n, int b, int e)
    {
        int i, j;
        
        for (i = 1; i <= n; i++) {                  //根据图的信息初始化dist数组
            dist[i] = DijGraph[b][i];
        }
        
        dist[b] = 0;
        ins[b] = true;                              //源节点加入集合S
        
        for (i = 2; i <= n; i++)                    //集合V一共有n-1个节点
        {
            int next = b;                           //记录此时V中最靠近源点的节点
            int minlen = 1000000;                   //以及源点到该节点的长度
            
            for (j = 1; j <= n; j++)                //源点从V中挑一个最近节点
            {
                if (ins[j]) continue;               //这个节点在S中,跳过
                
                if (dist[j] < minlen) {             //找到一个目前到源点距离最小的
                    
                    next = j;
                    
                    minlen = dist[j];               //更新离源点最近节点的信息
                }
            }
            
            ins[next] = true;                       //最近节点离开V加入S
            
            for (j = 1; j <= n; j++)                //新来的节点向外得到路径信息
            {                                       //并维护dist
                if (ins[j]) continue;
                
                /* 从源点到当前节点next的距离 + 当前节点next到某个相邻节点j的距离。*/
                /* 如果小于目前源点到节点j的最短距离dist[j],更新dist[j]。*/
                
                /* 另外,需要在这个时候记录到目的点的最短距离,以及路径条数。*/            
                
                int t = dist[next] + DijGraph[next][j];
                
                if (t <= dist[j])
                {
                    if (t < dist[j])
                    {
                        dist[j] = t;
                        
                        pre[j] = next;
                        
                        if (j == e) {
                            
                            cnt = 1;
                            
                            belen = dist[j];
                        }
                    }
                    
                    else
                    {
                        if (j == e) cnt++;
                    }
                }
            }
        }
    }
    
    int main()
    {
        int i, n, m, s, e;
        
        cin >> n >> m >> s >> e;
        
        int x, y, len;
        
        Initial(n);
        
        for (i = 1; i <= m; i++) {
            
            cin >> x >> y >> len;
            
            DijGraph[x][y] = len;
            DijGraph[y][x] = len;
        }
        
        Dijkstra(n, s, e);
        
        cout << belen << " " << cnt << endl;
        
        return 0;
    }
    
    

    2016/11/15

  • 相关阅读:
    加快火狐启动速度的几种方法 Leone
    我国的社保到底是多交好,还是少交好? Leone
    “情商”和“智商”究竟有哪些区别? Leone
    Atitti 知识图谱构建方法attilax 总结
    Atitit 知识图谱的数据来源
    Atitit java 二维码识别 图片识别
    Atitit 跨平台异常处理(2)异常转换 java c# js异常对象结构比较and转换
    Atitit 异常机制与异常处理的原理与概论
    Atitti knn实现的具体四个距离算法 欧氏距离、余弦距离、汉明距离、曼哈顿距离
    屏幕取词技术实现原理
  • 原文地址:https://www.cnblogs.com/qq952693358/p/6072145.html
Copyright © 2011-2022 走看看