计算单源最短路径的Dijkstra算法

最近开始看一些关于最短路径的算法,有广度优先遍历以及深度优先遍历等,除此之外,觉得最实用的还是求最小生成树还有求一些实际问题中两点之间的最短路径问题,在图中的边附上权值以后,就是不同于单纯遍历的场景了。在此,我看了下Dijkstra算法,它是求单源最短路径算法,同时记录了到任意一点最短路的路径,缺点的话感觉复杂度有点大,由于是采用邻接矩阵存储,所以为O(n^2)。
好了,现在主要说一下Dijkstra算法的主要思想,其针对每一步应该是贪心算法的体现,但就整体而言,又是动态最优化的体现,就不纠结这个了。其实,算法是将未获得最短路径的与已获得最短路径的分为两个集合,比如说起点选择1,总共有N个点,则从剩余N-1个点中选择与1点相邻且最小的权值边对应的顶点,并且将该点与点1归为同一类,关于归类可以用visited[N]数组来表示,初始均置为0,已确定的就置为1,在接下来的寻找点的过程中当然也要满足未访问(即visited[j]=0)并且也要是最小权值,这么说可能有点迷糊,不过只要记住一点就行了:每个点都会用dist[j]代表j点距离点1的最短距离,所以只要最短集合里加入了一个新的点,它所带来的改变就是会影响邻接与新加的点距离与点1的最短距离,即代码中的距离更新说明。那么接下来直接看代码吧。
这里写图片描述

#include <iostream>
using namespace std;
#define maxnum 100//最大顶点数
#define maxint 9999//最大权值


void Dijkstra(int n,int v,int *dist,int *prev,int c[maxnum][maxnum])
{
    bool s[maxnum];//s数组,即符合最短路径的集合
    memset(s,0,sizeof(s));//初始化均未访问
    for (int i=1;i<=n;i++)
    {
        dist[i] = c[v][i];
        if (dist[i]==maxint)
        {
            prev[i] = 0;
        }
        else
        {
            prev[i] = v;
        }
    }
    dist[v] = 0;
    s[v] = 1;//设置起始点已访问,且起始到起始点距离为0
    for (int i=2;i<=n;++i)//再寻找剩下的n-1个节点
    {
        int tmp = maxint;
        int u = v;
        for (int j=1;j<=n;++j)
        {
            if ((!s[j]) && dist[j]<tmp)//寻找邻接最短边以及对应点
            {
                u = j;
                tmp = dist[j];
            }
        }
        s[u] = 1;//收录该点

        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;//更新前驱节点
                }
            }
        }
    }
}
//利用一个数组保存prev的前驱节点路径,prev[j]表示第j点的前一个点
void MinPath(int *prev,int v,int n)
{
    int que[maxnum];
    int t = 2;
    que[1] = n;//第一个点即保存的是终点
    int tmp = prev[n];//终点前的一个点
    while (tmp != v)//只要没到起点则继续储存
    {
        que[t] = tmp;
        t++;
        tmp = prev[tmp];
    }
    que[t] = v;
    for (int j=t;j>=1;--j)//打印路径
    {
        if (j!=1)
        {
            cout<<que[j]<<"->";
        }
        else
            cout<<que[j]<<endl;
    }
}
/*数据为
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
*/
int main()
{
    int n,edgenum;//顶点,边数
    int dist[maxnum];
    int prev[maxnum];
    int c[maxnum][maxnum];
    while (cin>>n>>edgenum)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                c[i][j] = maxint;

        int p,q,weight;
        for (int i=1;i<=edgenum;i++)
        {
            cin>>p>>q>>weight;
            if (weight < c[p][q])
            { 
                c[p][q] = c[q][p] = weight;
            }
        }
        for (int i=1;i<=n;i++)
        {
            dist[i] = maxint;
        }
        for (int i=1;i<=n;++i)//打印权值矩阵
        {
            for (int j=1;j<=n;j++)
            {
                cout<<c[i][j];
                if (j == n)
                {
                    continue;
                }
                cout<<" ";
            }
            cout<<endl;
        }
        Dijkstra(n,1,dist,prev,c);
        cout<<"到最后一个点的最短路径长度为:"<<dist[n]<<endl;
        cout<<"从起点到最后一个点的路径为: ";
        MinPath(prev,1,n);
    }
    system("pause");
    return 0;
}

最终结果为
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值