最短路问题

在网图和非网图中,最短路径的含义是不同的。由于非网图它没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径;而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。

一、迪杰斯特拉( Dijkstra )算法

Dijkstra算法用于构建单源点的最短路径—,即图中某个点到任何其他点的距离都是最短的。例如,构建地图应用时查找自己的坐标离某个地标的最短距离。可以用于有向图,但是不能存在负权值。

我们以上图为例,通俗点说,这个迪杰斯特拉(Dijkstra) 算法,它并不是一下子求出了v0到v8的最短路径,而是一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到你要的结果。

Dijkstra算法设置一个集合S记录已求得的最短路径的顶点。

在构造的过程中还设置了个辅助数组:

dist[]:记录从源点v0到其他各顶点当前的最短路径长度,它的初态为:若从v0到vi;有弧,则dist[i]为弧上的权值;否则置dist[i]为∞。

使用邻接矩阵或者带权的邻接表表示时,时间复杂度为O(V^2)。

完整代码(求一个点与各个点之间的最短路径)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define MaxInt 32767  //表示极大值
#define MVNum 100 //最大顶点数
#define OK 1
typedef char VerTexType;
typedef int  ArcType;
typedef int  status;
typedef int OtherInfo;                        //和边相关的信息 
typedef struct
{
    char vexs[100];
    int arcs[100][100];
    int vexnum,arcnum;
}AMGraph;
status LocateVex(AMGraph G,VerTexType u)
{
    int i;
    for(i=0;i<G.vexnum;i++)
      if(u==G.vexs[i]) return i;
    return -1;  
}
//邻接矩阵表示法
status CreateUDN(AMGraph &G)
{
    cout <<"请输入总顶点数,总边数中间以空格隔开:";
   cin>>G.vexnum>>G.arcnum;   
   cout << "输入点的名称 " <<endl;
   for(int i=0;i<G.vexnum;i++)
     cin>>G.vexs[i];          
     
   for(int i=0;i<G.vexnum;i++)    //初始化邻接矩阵,边的权值均为Maxint 
      for(int j=0;j<G.vexnum;j++)
        G.arcs[i][j]=MaxInt;
    for(int k=0;k<G.arcnum;k++)   //构造邻接矩阵 
    {
        char v1,v2;
        int w;
        cin>>v1>>v2>>w;       //输入一条边依附的顶点及权值 
        int i=LocateVex(G,v1);
        int j=LocateVex(G,v2); //确定v1,v1在G中的位置,即顶点数组的下标 
        G.arcs[i][j]=w;       //边<v1,v2>的权值置为w 
        G.arcs[j][i]=G.arcs[i][j];    //置<v1,v2>的对称边<v2,v1>的权值为w    
    }
    return OK;
}

void ShortestPath_DTJ(AMGraph G,int v0){
    int n=G.vexnum;//顶点数 
    int S[n],Path[n],i,v,w,D[n],min,sum;
    for(int v=0;v<n;v++){
        S[v]=0;D[v]=G.arcs[v0][v];//S[i]=1表示点已知 
        if(D[v]<MaxInt)   //有弧 
            Path[v]=v0;  //点的前驱 
        else Path[v]=-1;
    }
    S[v0]=1;D[v0]=0;
    //初始化结束 
    for( i=1;i<n;i++){
        min=MaxInt;
        for( w=0;w<n;w++){
            if(!S[w]&&D[w]<min)
                v=w;min=D[w];
        }
        S[v]=1;
        for(w=0;w<n;w++){
            if(!S[w]&&(D[v]+G.arcs[v][w])<D[w]){
                D[w]=D[v]+G.arcs[v][w];
                Path[w]=v;
            }
        }
    }
    for (int i=0;i<n;i++)
    cout<<G.vexs[v0]<<"---->"<<G.vexs[i]<<":"<<D[i]<<endl;
    cout<<D[4];
}
int main(){
    AMGraph G;
    CreateUDN(G);
    ShortestPath_DTJ(G,0);
    return 0;
}
/*
5 7
0 1 2 3 4
0 1 10
0 2 3
0 3 20
1 3 5
2 1 2
2 4 15
3 4 11
结果:18 
*/

二、弗洛伊德( Floyd )算法

 Floyd算法相对于Dijkstra算法来说,可以解决多源最短路径问题(即可以从任意一个点到任意一个点),可应用于地图导航走最短路径、为各城市修建最短路径的通信网(节省成本)等问题,时间复杂度是O(n3)

优点:比较容易容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。

缺点:时间复杂度比较高(n3),不适合计算大量数据,当数据稍微大点儿的时候就可以选择其他的算法来解决问题了,不然也会是超时。

Floyd算法与Dijkstra算法的不同

1.Floyd算法是求任意两点之间的距离,是多源最短路,而Dijkstra(迪杰斯特拉)算法是求一个顶点到其他所有顶点的最短路径,是单源最短路。

2.Floyd算法属于动态规划,我们在写核心代码时候就是相当于推dp状态方程,Dijkstra(迪杰斯特拉)算法属于贪心算法。

3.Dijkstra算法时间复杂度一般是o(n^2),Floyd算法时间复杂度是o(n^3),Dijkstra(迪杰斯特拉)算法比Floyd算法块。

Floyd算法可以算带负权的,而Dijkstra(迪杰斯特拉)算法是不可以算带负权的。并且Floyd算法不能算负权回路。

原理:

假设有向图G=(V,E)采用邻接矩阵存储。设置一个二维数组A用于存放当前顶点之间的最短路径长度,分量A[i][j]表示当前顶点i -> j的最短路径长度。然后,每次添加一个顶点,同时对A的数组进行筛选优化,期间会产生k个A数组。Ak[i][j]数组代表着从考虑0 -> k的i -> j 的最小距离,当k 等于全部顶点数的时候,就是已经找出了i -> j 的最短距离。

具体的实现手段(算法思想)

1、每一个顶点v,与任意一个顶点队(i,j),其中i≠j,v≠i,v≠j

如果存在A[i][j] > A[v][j] + A[i][v]

则将A[i][j]的值换为:A[v][j] + A[i][v],同时path[i][j]的值也换为v

2、然后依此对每一个顶点进行上述操作

3、最后得到path数组的值,就是咱们需要的最短路径的顶点坐标,再根据顶点,查找对应的A数组的值(权值),就能得到所谓的最短路径

4、最终俩个数组的意义

二维数组A:对应的是更新过后的俩点之间最短路径的一个权值

二维数组Path:对应的是更新过后俩点之间的最短路径所经历的点坐标

A[i][j] > A[v][j] + A[i][v]这个公式的目的就是求出最短的那个路径,比如在

i=1,j=2,v=3的时候,只要上述的比较公式成立,就证明了目前1到2的最短路径为1->3->2,而不是直接的1->2

完整代码(求每个顶点之间的最短路径)

#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
#define MaxVertexNum 100
#define INF 32767
typedef struct
{
        char vertex[MaxVertexNum];
        int edges[MaxVertexNum][MaxVertexNum];
        int n,e;
}MGraph;
void CreateMGraph(MGraph &G)
{
     int i,j,k,p;
     cout << "请输入顶点数和边数:"; 
     cin >> G.n >> G.e;
     cout << "请输入顶点元素:";
     for(i = 0;i < G.n;i++)
         cin >> G.vertex[i];
     for(i = 0;i < G.n;i++)
           for(j = 0;j < G.n;j++)
           {
                 G.edges[i][j] = INF;
                 if(i == j)
                      G.edges[i][j] = 0;
           }
     for(k = 0;k < G.e;k++)
     {
           cout << "请输入第" << k+1 << "条弧头弧尾序号和相应的权值:";
           cin >> i >> j >> p;
           G.edges[i][j] = p;
     }
}
void Ppath(MGraph &G,int path[][MaxVertexNum],int i,int j)
{
     int k;
     k = path[i][j];
     if (k == -1)
        return;
     Ppath(G,path,i,k);
     printf("%c",G.vertex[k]); 
     Ppath(G,path,k,j);
}
void Dispath(MGraph &G,int A[][MaxVertexNum],int path[][MaxVertexNum],int n)
{
     int i,j;
     for(i = 0;i < n;i++)
     for(j = 0;j < n;j++)
     {
           if(A[i][j] == INF)
           {
                if(i != j)
                     printf("从%d到%d没有路径\n",i,j);
           }
           else
           {
               printf("从%c到%c=>路径长度:%d 路径:",G.vertex[i],G.vertex[j],A[i][j]);
               printf("%c",G.vertex[i]);
               Ppath(G,path,i,j);
               printf("%c\n",G.vertex[j]);
           }
     }
}
void Floyd(MGraph &G)
{
     int i,j,k;
     int A[MaxVertexNum][MaxVertexNum];
     int path[MaxVertexNum][MaxVertexNum];
     for(i = 0;i < G.n;i++)
           for(j = 0;j < G.n;j++)
           {
                 A[i][j] = G.edges[i][j];
                 path[i][j] = -1;
           }
     for(k = 0;k < G.n;k++)
     for(i = 0;i < G.n;i++)
     for(j = 0;j < G.n;j++)
           if(A[i][j] > A[i][k] + A[k][j])
           {
                      A[i][j] = A[i][k] + A[k][j];
                      path[i][j] = k; 
           }
     Dispath(G,A,path,G.n);
}
int main()
{
    MGraph G;
    CreateMGraph(G);
    Floyd(G);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值