图——最短路径

      前言:最短路径是图论中很经典的一个问题,给定图G(V,E),求一条从起点到终点的路径,使得这条路径上所经过的所有边的边权之和最小。解决最短路径的经典算法有4个,分别是dijkstra算法,bellman-ford算法,spfa算法,floyd算法,这些算法可应用于不同的题目,今天先来讲解dijkstra算法。

1.dijkstra算法的思想

      dijkstra算法主要用来解决单源最短路径问题,且仅适用于所有边权非负的情况,如果边权为负的话要用spfa算法解决。基本思想是对图G[V,E]设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点u,访问并加入集合S,之后令u为中介点,优化s与所有u能到达的顶点v之间的最短距离。这样的操作n次,直到集合S已包含所有顶点。

2.dijkstra算法的具体实现

      其思想中最为关键的两个东西就是集合S的实现和起点s到达顶点v的最短距离的实现。

      @集合S可以用一个bool数组vis[]实现,当为true时表示已经访问过(和之前的图的遍历中表示的意义一样),为false时表示未访问

      @起点s到顶点v的最短距离可以用一个int型数组d[]来表示,初始化时要特别注意除了起点s的d[s]初始化为0,其他顶点均初始化为inf(一个很大的数),表示不可到达,在接下来的算法中一点一点的来优化d[],当所有的顶点都被遍历过时就可以得出最短路径的长路了。

      因为图的存储形式有邻接矩阵和邻接表两种,所以对应的算法实现也有两种。但这两种写法都是基于上面的思想,且实现过程基本相同,唯一的不同仅在于枚举从u出发所能到达的所有顶点v上:邻接矩阵需要枚举所有的顶点来查看v是否和u连通;邻接表则是可以直接得到u所能到达的所有顶点v,而不需要遍历所有的顶点,只需要访问与之相连通的顶点。

3.dijkstra算法的邻接矩阵版

const int maxv=1000;
const int inf=100000000;

int n,G[maxv][maxv];
int d[maxv];
bool vis[maxv]={false};

void Dijkstra(int s)//s为起点
{
    fill(d,d+maxv,inf);//初始化d数组
    d[s]=0;            //起点s到自身的距离为0
    for(int i=0;i<n;i++)//循环n次
    {
        int u=-1,mina=inf;
        for(int j=0;j<n;j++)//每次找出未被访问的顶点中离起点最短距离最小的顶点
        {
            if(vis[j]==false&&d[j]<mina)
            {
                u=j;
                mina=d[j];
            }
        }
        
        if(u==-1) return;//若找不到,这说明剩下的顶点和起点s不连通或者说已经找完了所有顶点
        vis[u]=true;//若找到则进行标记
        for(int v=0;v<n;v++)//找从u出发能到达的顶点v,是否能优化d[v] 
        {
            if(vis[v]==false&&G[u][v]!=inf&&d[u]+G[u][v]<d[v])
                d[v]=d[u]+G[u][v];
        }
        
    }
}

3.dijkstra算法的邻接表版

const int maxv=1000;
const int inf=100000000;
struct Node{
    int v,dis;
};

int n;
vector<Node> adj[maxv];
int d[maxv];
bool vis[maxv]={false};

void Dijkstra(int s)//s为起点
{
    fill(d,d+maxv,inf);//初始化d数组
    d[s]=0;            //起点s到自身的距离为0
    for(int i=0;i<n;i++)//循环n次
    {
        int u=-1,mina=inf;
        for(int j=0;j<n;j++)//每次找出未被访问的顶点中离起点最短距离最小的顶点
        {
            if(vis[j]==false&&d[j]<mina)
            {
                u=j;
                mina=d[j];
            }
        }
        
        if(u==-1) return;//若找不到,这说明剩下的顶点和起点s不连通或者说已经找完了所有顶点
        vis[u]=true;//若找到则进行标记
       
        for(int j=0;j<adj[u].size();j++)//与邻接矩阵访问不相同的地方,读者应仔细揣摩此处,
                                            对比理解记忆。
        {
            int v=adj[u][j].v;
            if(vis[v]==false&&adj[u][j].dis+d[u]<d[v])
                d[v]=d[u]+adj[u][j].dis;
        }
    }
}


上面的两种实现都是基于有向图的,即是单向边,如果是无向图怎么办呢???

其实很简单,只需要把无向边当成两条指向相反的有向边即可,G[u][v]和G[v][u]赋以相同的边权;adj[u]末尾加上v,adj[v]加上u.

下一个博客接着写最短路径的进一步应用~~~

猜你喜欢

转载自blog.csdn.net/qq_38938670/article/details/83421005