南京地铁最短路径以及最少换乘算法C++不用类

迪杰斯特拉算法应用于南京地铁求最短路径

深度遍历求图中所有路径

定义的变量名及其作用

变量名称作用
int maxint 99999无法达到的数
int maxnum 300用来初始化二维数组
int prev[maxnum]记录当前点的前一个结点
int c[maxnum][maxnum]记录图的两点间路径长度,初始化所有值为maxint
int dist[maxnum]表示当前点到源点的最短路径长度,初始为maxint
int n图的结点数
int line图的路径数
int s[maxnum]用来判断是否已经遍历过,初始为0

然后迪杰斯特拉遍历图是单独放入一个函数中去,需要传入的变量有

  1. n 总共的顶点数
  2. 开始的源点 v
  3. dist[maxnum]
  4. prev[maxnum]
  5. c[maxnum][maxnum]
    首先定义一个bool类型的数组,用来判断是否已经遍历
    先第一次遍历所有结点,得到所有结点到源点的距离,并且将s[maxnum]的初始值都设为0,表示初始一个点都没有使用,因为dist[maxnum]的初始值都为maxint,在第一次遍历之后,如果dist[i]依旧为maxint的点就是与源点不相邻的,否则源点v就是此点的前一个结点,存v到prev数组中去。prev[i]=v;
    赋值dist[v]的值为0,s[v]的值为1。将源点剔除。
    第二次的二重遍历的目的是,依次将未放入s集合的节点中dist[]值最小的结点放入s中。当s包含了所有的顶点后,dist就记录了从源点到所有其他顶点之间的最短距离。
    每次循环只能标记一个顶点
    可以使用一个例子来说明这个算法的核心

例如:将一个简单的图的顶点都放入数组中去

dist[]存储所有点到源点的距离,比如这里求1到所有点的路径,那么源点就是1

《南京地铁最短路径以及最少换乘算法C++不用类》 例子

dist中存的就是[0,1,2,max,max,max]因为4,5,6,都与1不相邻,那就存储距离为max

具体流程 如下:

dist[maxnum]=[0,1,2,max,max,max]  //其中只有1被标记
dist[maxnum]=[0,1,2,max,max,max]  //找到现在dist最小的,为1,那就标记2
dist[maxnum]=[0,1,2,3,max,max]   //因为与2相邻的是1和4,但是1已经被标记了,那就更新4
dist[maxnum]=[0,1,2,3,max,max]   //依旧是找到没有标记的元素中距离最小的,现在为3
    //如此知道所有元素都被标记   就可以得到所有路径距离

下一个算法是使用深度遍历,求出所有路径。给出简单流程图

《南京地铁最短路径以及最少换乘算法C++不用类》 流程图

直接放源代码。。。。。虽然是C++,但是我一个类都没有使用。不是不想,而是忘了如何使用。

具体的邻接矩阵可以根据自己需求删改

#include <iostream>
#include<string>
using namespace std;

#define max 200

const int maxnum = 300;
const int maxint = 999999;
string str[]={" ","迈皋桥","红山动物园","南京站","新模范马路","玄武门","鼓楼","珠江路",
    "新街口","张府园","三山街","中华门","安德门","天隆寺",
    "软件大道","花神庙","南京南站","双龙大道","河定桥","胜太路",
    "百家湖","小龙湾","竹山路","天印大道","龙眠大道","江苏经贸学院",
    "南京交院","中国医药科大学","经天路","南大仙林校区","羊山公园",
    "仙林中心","学则路","仙鹤门","金马路","马群","钟灵街","孝陵卫",
    "下马坊","苜蓿园","明故宫","西安门","大行宫","上海路","汉中门",
    "莫愁湖","云锦路","集庆门大街","兴隆大街","奥体东","元通站","雨润大街",
    "油坊桥","林场","星火路","东大成贤学院","泰冯路","天润路","柳州东路",
"上元门","五塘广场","小市","新庄","鸡鸣寺","浮桥","常府街","夫子庙",
"武定门","雨花门","卡子门","大明路","明发广场","宏运大道","盛泰西路",
"天元西路","九龙湖","诚信大道","东大九龙湖校区","秣周东路","仙林湖","桦墅",
"孟北","东流","灵山","汇通路","苏宁总部徐庄","聚宝山","王家湾","蒋王庙","岗子村",
"九华山","云南路","草场门","龙江","金牛湖","八百桥","沈桥","方洲广场","凤凰山公园","雄州",
"龙池","六合开发区","化工园","长芦","葛塘","大厂","卸甲囤","信息工程大学","高新开发区","泰山新村",
"雨山路","文德路","龙华路","南京工业大学","浦口万汇城","临江","江心洲","绿博园","梦都大街","奥体中心",
"中胜","小行","黄里","桥林西","桥林新城","北顺圩","步月路","滨江村","生态科技园","天保路",
"新梗村","天河路","黄河路","中和街","华新璐","春江新城","铁心桥大街","景明佳园",
"无想山","金龙路","中山东路","溧水","团山","金山","柘塘站","禄口机场","翔宇路南","铜山",
"石湫","明觉","高淳","高淳站","翔宇路北","正方中路","吉印大道","河海大学佛城西路","翠屏山"};


int path[max];
int visited[max]={0};
int ck[max];
int h=0;
int mk[max][max];
int cm[max];
int index[max];

void DFS(int a[][156],int u,int v,int k)
{   
    k++;
    visited[u]=1;   //标记每一次遍历的初始点防止出现路径中有重复点的情况
    path[k]=u;   //将当前起始点存入路径数组
    if(u==v)       //到了路径的结尾
    {
        int m=0;
        for(int i=0;i<=k;i++){
            mk[h][i]=path[i];    //将每一条完整路径存入二维数组的一行

        }
        
        for(int j=0;j<=k;j++)
            {
                if(mk[h][j]==3&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==63&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==42&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==6&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==8&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==16&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==12&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==34&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==50&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==52&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
                if(mk[h][j]==56&&(mk[h][j-1]-mk[h][j+1]>2||mk[h][j-1]-mk[h][j+1]<-2))m++;
            }
        


            
        cm[h]=m;    //每一条路径的换乘次数
        ck[h]=k;    //每一条路径的长度
        h++;        //二维数组下移一行
        
    }
    else       //如果没到结尾那就从其他所有与起始点相邻的点重新开始遍历
    {
        for(int i=1;i<=156;i++)
        {
            if(a[u][i]==1&&visited[i]==0)
            {
                DFS(a,i,v,k);
            }
        }
    }
    visited[u]=0;   //将起始点的标记取消,因为是求所有路径
}

int findmin(int arr[],int n)
{   
    int min=arr[0];
    
    for(int i =0;i<n;i++)
    {
        if(arr[i]<min)
        {
            min=arr[i];
        }
    }
    return min;
}

int findindex(int arr[],int n)
{
    int min = arr[0];
    int index= 0;
    for(int i = 0;i<n;i++)
    {
        if(arr[i]<min)
        {
            index=i;
        }
    }
    return index;
}
int findmink(int arr1[],int arr2[],int n)
{
    int min=arr1[arr2[0]];
    
    for(int i=0;i<n;i++)
    {
        if(arr1[arr2[i]]<min)
        {
            
            min=arr1[arr2[i]];
            
        }
    }
    return min;
}
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];//别的点到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(i=1; i<=n-1; 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保存当前邻接点中距离最小的点的号码  u随之变成没有作为顶点的下标 
                tmp = dist[j];
            }
        s[u] = 1;    // 表示u点已存入S集合中
 
        // 更新dist
        //以u为顶点更新数据 
        for(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;
                }
            }
    }
}
 
//searchPath(prev, 1, i);
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 << str[que[i]] << " -> ";
        else
            cout << str[que[i]] << endl;
}
void init()
{
    for(int i=1;i<=156;i++)
    {
        if(i==1){cout<<"    1号线:"<<endl;}
        if(i==28){cout<<endl<<"    2号线:"<<endl;}
        if(i==53){cout<<endl<<"    3号线:"<<endl;}
        if(i==79){cout<<endl<<"    4号线:"<<endl;}
        if(i==94){cout<<endl<<"    s8号线:"<<endl;}
        if(i==110){cout<<endl<<"   10号线:"<<endl;}
        if(i==122){cout<<endl<<"    s3号线:"<<endl;}
        if(i==138){cout<<endl<<"    s7号线:"<<endl;}
        cout<<str[i]<<":"<<i<<"   ";
        if(i%5==0&&i>=5)
        {
            cout<<endl;
        }
    }
    cout<<endl;
    cout<<"please choose the number of the begin or the end"<<endl;
}
int ifoneline(int b,int e)
{
    int m;
    int n;
    if(b>=1&&b<=27)
        m=1;
    if(b>=28&&b<=52)
        m=2;
    if(b>=53&&b<=78)
        m=3;
    if(b>=79&&b<=93)
        m=4;
    if(b>=94&&b<=109)
        m=5;
    if(b>=110&&b<=121)
        m=6;
    if(b>=122&&b<=137)
        m=7;
    if(b>=138&&b<=156)
        m=8;
    if(e>=1&&e<=27)
        n=1;
    if(e>=28&&e<=52)
        n=2;
    if(e>=53&&e<=78)
        n=3;
    if(e>=79&&e<=93)
        n=4;
    if(e>=94&&e<=109)
        n=5;
    if(e>=110&&e<=121)
        n=6;
    if(e>=122&&e<=137)
        n=7;
    if(e>=138&&e<=156)
        n=8;
    if(m==n)
        return 0;
    if(m!=n)
        return 1;
}
int main()
{
    cout<<"请选择最短路径或者最少换乘,如果是最短路径则choose1,如果是最少换乘则choose2,choose=:";

    int b,e,choose;
    cin>>choose;
    init();

    cout<<"please input the begin"<<endl;
    cin>>b;
    cout<<"please input the end"<<endl;
    cin>>e;

    freopen("ditie.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两点及其路径长度
 
    
    for(int i=1; i<=n;i++)
        for(int j=1; j<=n; j++)
            c[i][j] = maxint;
 
    for(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,这样表示无向图
        }
    }//将数据更加精确 
    fclose(stdin);
    for(i=1; i<=n; i++)
        dist[i] = maxint;
    int a[156][156];
    freopen("output.txt","r",stdin);
    for(i=1;i<=156;i++)



    {
        for(int j=1;j<=156;j++)
        {
            
            cin>>a[i][j];
        }
    }
    int k =-1;
    switch(choose)
    {
    case 1:Dijkstra(n, b, dist, prev, c);cout<<"the shortest distance is ";cout<<dist[e]<<endl;searchPath(prev,b,e);break;
    case 2:DFS(a,b,e,k);fclose(stdin);int f=0;
    for(int j=0;j<h;j++)
    {
        if(cm[j]==findmin(cm,h))
        {
            index[f]=j;
            f++;
        }
    }
    int minindexk;
    for(j=0;j<f;j++)
        if(ck[index[j]]==findmink(ck,index,f))
            minindexk=index[j];
    cout<<"========================"<<endl;
    cout<<"最少换乘中的最短路径为:";
    for(i =0;i<=ck[minindexk];i++)
    {
        cout<<str[mk[minindexk][i]]<<"   ";
    }
    cout<<endl;
    if(ifoneline(b,e)==1)
    {cout<<"换乘次数为  :"<<cm[minindexk]<<"  ";}
    else
    {cout<<"换乘次数为  :"<<0<<" ";
    }
    cout<<"the distance is  :"<<ck[minindexk]<<endl;break;
    }
    return 0;
}

给出两个如何存储邻接矩阵的样图
注意:输入都是通过txt文本,要将文本和程序放在同一个路径中
点之间的路径关系如下:

《南京地铁最短路径以及最少换乘算法C++不用类》 2018-11-10_232105.png

邻接矩阵千万不要自己根据图一个个的输,可能 会输到80岁。。。可以换一种思路,用点路关系自动生成。具体不说了,稍微想一下就能懂,实在想不出来就放弃。

《南京地铁最短路径以及最少换乘算法C++不用类》 2018-11-10_232205.png

    原文作者:快乐的sunxth
    原文地址: https://www.jianshu.com/p/9346eab37f19
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞