图的最短路径问题(Dijskra算法)(C/C++)

问题描述:

在给定的带权有向图G=(V,E)中,求V集合的中源点v0到其余各点的最短路径。

思路:

设置一个集合S存放已经找到最短路径的顶点(初始时先将源点v0存入),
对于其余顶点,每次都会找一个从源点出发路径最短的顶点,就将其放入集合中,
最新放入集合的顶点会成为源点v0通往其他顶点的辅助顶点,
即源点v0可以借助集合中的其他顶点到达集合外的某一顶点,但不可以借助集合外的任意顶点到达其他任意顶点。
重复上述过程,直到所有顶点都放入集合中。

辅助变量:
  • 定义一个数组dist,其下标对应顶点数组下标,表示每个顶点到源点v0之间的最短路径长度,不存在则设为无穷大。
  • 定义一个数组path,其下标对应顶点数组下标,表示每个顶点与哪个顶点之间的路径最小。初始都设为-1。该数组用于最后打印最短路径,假设源点0到顶点1之间的最短路径为0→4→1,则可以通过数组path,先找到path[1],其值为与顶点1之间权值最小的顶点的下标,即4,则再找到path[4],其值为与顶点4之间权值最小的顶点的下标,即源点0,这样便获得了最短路径,可以看出这个过程是先进后出的过程,所以在获取路径的过程中可以用栈结构来存储。
  • 定义一个数组s表示集合S,true代表该顶点在集合中,false代表该顶点不再集合中。
大致流程:

1、初始化三个数组

2、while(数组s的元素个数小于图的顶点个数)

2.1、在数组dist中求最小值,其下标为m

2.2、将顶点m添加到集合S中(即将数组s对应下标标记为true)

2.3、检查借助顶点m能否使源点到达各个顶点的路径更短,能则修改数组dist和path

代码实现:
//图结构的定义
typedef struct AMGraph
{
    int vex[ARRAYSIZE]={0};//顶点数组
    int arc[ARRAYSIZE][ARRAYSIZE];//邻接矩阵,表示顶点之间的关系
    int vexnum=0,arcnum=0;//顶点个数和弧个数
    int type=0;//1表示无向图,2表示有向图,3表示无向网,4表示有向网
}AMGraph;

int LocateVex(AMGraph &G,int e); //获取顶点位置,返回顶点e在顶点数组中的下标
//打印最短路径
void DisplayPath(AMGraph &G,vector<int> &path,vector<int> &dist,int v)
{
    stack<int> s;
    int p=0;
    for(int i=0;i<G.vexnum;++i)
    {
        if(i==v)
        {
            continue;
        }
        if(dist[i]==INT_MAX)
        {
            cout << "源点" << v << "到顶点" << G.vex[i] << "不存在路径" << endl;
            continue;
        }
        p=path[i];
        s.push(p);
        while(p!=v)
        {
            p=path[p];
            s.push(p);
        }
        cout << "源点" << v << "到顶点" << G.vex[i] << "的最短路径为:";
        while(!s.empty())
        {
            cout << G.vex[s.top()] << "→";
            s.pop();
        }
        cout << G.vex[i] << endl;
        cout << "路径长度为:" << dist[i] << endl;
    }
}
//Dijskra算法
void Dijskra(AMGraph &G,int v)
{
    if(G.type!=4)
    {
        cout << "图类型有误,该函数只适用于带权有向图" << endl;
        return;
    }
    //获取源点下标
    v=LocateVex(G,v);
    if(v==-1)
    {
        cout << "图中不存在该顶点" << endl;
        return;
    }
    //建立与初始化数组
    vector<int> dist,path;
    for(int i=0;i<G.vexnum;++i)
    {
        dist.push_back(G.arc[v][i]);
        if(G.arc[v][i]!=INT_MAX)
            path.push_back(v);
        else
            path.push_back(-1);
    }
    vector<bool> s(G.vexnum,false);
    s[v]=true; //表示源点已处理,即源点在集合S中
    int min=INT_MAX,m=-1;//用于存储dist数组的最小值及其下标
    for(int n=1;n<G.vexnum;++n)
    {
        //寻找dist数组中的最小值
        for(int i=0;i<G.vexnum;++i)
        {
            if(s[i]==true) continue;
            if(dist[i]<=min)
            {
                min=dist[i];
                m=i;
            }
        }
        s[m]=true;//将找到的顶点标记已处理
        //修改数组dist和path:
        //检查每个未处理的顶点,
        //检查“当前源点到各个顶点的长度”是否比“源点借助刚找到的顶点m到达各个顶点的长度”要长
        //如果借助顶点m会使源点与某顶点之间的路径更短,则修改某顶点对应的dist值和path值
        for(int i=0;i<G.vexnum;++i)
        {
            //dist[i]:顶点i到源点的长度;dist[m]:顶点m到源点的长度;G.arc[m][i]:顶点m到顶点i的长度.
            //BUG记录:若G.arc[m][i]为无穷大,再添加dist[m]会溢出,导致算法出错
            if(s[i]==false && G.arc[m][i]!=INT_MAX && dist[i]>dist[m]+G.arc[m][i])
            {
                dist[i]=dist[m]+G.arc[m][i];
                path[i]=m;
            }
        }
        min=INT_MAX;
        m=-1;
    }
    //打印最短路径
    DisplayPath(G,path,dist,v);
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值