数据结构之图的应用(二)之最短路径

前面我们一期文章我们分享了图的应用中的一种——最小生成树。现在我们开始来讲讲第二种——最短路径。最短路径问题在我们日常生活中也是很常见的。比如我们从一个地点要去另一个地点玩,路径可以有千千万万条,但我们要选择出最短路径来节省时间或者金钱等等。在我们算法学习中,最短路径问题也有涉及到几个算法。主要是两个经典算法——Dijkstra迪杰斯特拉算法和Floyd弗罗伊德算法。下面让我们一起来看看吧。

一.单源最短路径——Dijkstra迪杰斯特拉算法(主要用于求解某一个顶点到达全部顶点的最短路径)

核心思路:

 

 核心代码(C语言版):

//邻接矩阵
#include <stdio.h>
#define MAXSIZE 100
#define INFINITY 65535  //表示无边(Weight有时为0,不用0表示)
typedef char VertexType;
typedef int EdgeType;
typedef struct
{
    VertexType vexs[MAXSIZE];   //顶点表
    EdgeType arc[MAXSIZE][MAXSIZE]; //邻接矩阵
    int numVertexes, numEdges;  //顶点数和边数
}MGraph;
void CreateMGraph(MGraph* G)
{
    int i, j, k, w;
    printf("输入顶点数和边数:\n");
    scanf("%d%d", &G->numVertexes, &G->numEdges);
    for (i = 0; i < G->numVertexes; i++)
        scanf("%c", &G->vexs[i]);
    //初始化邻接矩阵
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = INFINITY;
    for (k = 0; k < G->numEdges; k++)
    {
        printf("输入边(vi, vj)的下标i和上标j和权w:\n");
        scanf("%d%d%d", &i, &j, &w);
        G->arc[i][j] = w;
        G->arc[j][i] = G->arc[i][j];   //无向图矩阵对称
    }
}
//迪杰斯特拉算法生成最短路径
//求解有向网G的v0顶点到其余顶点v最短路径P[v]及带权长度D[v]
//P[v]:前驱下标顶点,D[v]:v0到v的最短路径长度
#define  MAXVEX 9
typedef int Patharc[MAXVEX];    //存储最短路径下标
typedef int ShortPathTable[MAXVEX]; //最短路径权值和
void Dijkstra(MGraph G, int v0, Patharc* P, ShortPathTable* D)//整个算法核心部分
{
    int v, w, k, min;
    int final[MAXVEX];  //final[w]=1表示求得v0至vw最短路径
    //初始化数据
    for (v = 0; v < G.numVertexes; v++)
    {
        final[v] = 0;
        (*D)[v] = G.arc[v0][v]; //将与v0有连线的顶点加上权值。邻接矩阵
        (*P)[v] = 0;
    }
    (*D)[v0] = 0;
    final[v0] = 1;
    //每次求得v0到某个顶点的最短路径
    for (v = 1; v < G.numVertexes; v++)
    {
        min = INFINITY;    //无穷大值
        //找到当前离v0最近的点(未求得最短路径的),用k记录其下标
        for (w = 0; w < G.numVertexes; w++)
        {
            if (!final[w] && (*D)[w] < min)
            {
                k = w;
                min = (*D)[w];
            }
        }
        final[k] = 1;
        //修正邻接点当前最短路径及距离
        for (w = 0; w < G.numVertexes; w++)
        {
            //若以w为前驱顶点的路径更短,更新路径和放有前驱顶点的数组P[v]
            if (!final[w] && (min + G.arc[k][w] < (*D)[w]))
            {
                (*D)[w] = min + G.arc[k][w];
                (*P)[w] = k;
            }
        }
    }
}

2.所有顶点间的最短路径——Floyd弗罗伊德算法

核心思路:

主要步骤: 

求图中任意顶点vi到vj的最短路径。
① 将vi到vj的最短路径长度初始化为对应邻接矩阵G.arcs[i][j]值,然后进行如下n次比较和修正:
② 在vi和vj间加入顶点v0,比较(vi,v0,vj)和(vi,vj)的路径长度,取较短的路径作为vi到vj的当前最短路径,如此类推依次加入v1、v2…经过n次比较和修正,得到vi到vj的最短路径。
具体参考下图例子:

结果加完所有顶点后的表格就表示所有顶点间的最短路径啦。

完整代码:

/*图的最短路径——Floyd算法*/
# include<stdio.h>
# define MAX_VERTEX_NUM 20            //最多顶点个数
# define INFINITY 32768
/*图的邻接矩阵表示法*/
typedef int AdjType;
typedef char VertexData;
typedef struct ArcNode 
{
    AdjType adj;                            //无权图用1或0表示是否相邻,带权图则为权值类型
}ArcNode;
typedef struct 
{
    VertexData vertex[MAX_VERTEX_NUM];                //顶点向量
    ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];    //邻接矩阵
    int vexnum, arcnum;                                //图的顶点数和弧数
}AdjMatrix;
/*采用邻接矩阵表示法创建有向网*/
/*求顶点位置*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    int k;
    for (k = 0; k < G->vexnum; k++) {
        if (G->vertex[k] == v)
            break;
    }
    return k;
}
/*创建有向网*/
int CreateDN(AdjMatrix* G) {
    int i, j, k, weight;
    VertexData v1, v2;
    printf("输入图的顶点数和弧数:");        //输入图的顶点数和弧数
    scanf("%d%d", &G->vexnum, &G->arcnum);
    for (i = 0; i < G->vexnum; i++) {        //初始化邻接矩阵
        for (j = 0; j < G->vexnum; j++)
            G->arcs[i][j].adj = INFINITY;
    }
    printf("输入图的顶点:");
    for (i = 0; i < G->vexnum; i++)            //输入图的顶点
        scanf(" %c", &G->vertex[i]);
    for (k = 0; k < G->arcnum; k++) {
        printf("输入第%d条弧的两个顶点及权值:", k + 1);
        scanf(" %c %c %d", &v1, &v2, &weight);    //输入一条弧的两个顶点及权值
        i = LocateVertex(G, v1);
        j = LocateVertex(G, v2);
        G->arcs[i][j].adj = weight;            //建立弧
    }
}
/*顶点集合,用于存放顶点和路径*/
typedef struct {
    char elem[MAX_VERTEX_NUM];
    int last;
}PathSet[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
PathSet path;                                //path[i][j]中存放vi到vj的当前最短路径
/*路径集合初始化*/
void InitPathSet(PathSet path, int i, int j) {
    path[i][j].last = -1;
}
/*路径集合添加操作*/
void AddPath(PathSet path, int i, int j, char c) {
    path[i][j].last++;
    path[i][j].elem[path[i][j].last] = c;
}
/*两个路径集合path[i][k]和path[k][j]合并操作*/
void Join(PathSet path, int i, int j, int k) {
    int t;
    for (t = 0; t <= path[i][k].last; t++)
        path[i][j].elem[t] = path[i][k].elem[t];
    for (t = path[i][k].last + 1; t <= path[i][k].last + path[k][j].last; t++)
        path[i][j].elem[t] = path[k][j].elem[t - path[i][k].last];
    path[i][j].last = path[i][k].last + path[k][j].last;
}
int dist[MAX_VERTEX_NUM][MAX_VERTEX_NUM];    //dist[i][j]为vi到vj的当前最短路径长度
/*Floyd算法*/
void ShortestPath_DJS(AdjMatrix G) {
    int i, j, k;
    for (i = 0; i < G.vexnum; i++) {        //初始化dist[i][j]和path[i][j]
        for (j = 0; j < G.vexnum; j++) {
            InitPathSet(path, i, j);
            dist[i][j] = G.arcs[i][j].adj;
            if (dist[i][j] < INFINITY) {
                AddPath(path, i, j, G.vertex[i]);//若vi和vj之间有路径,则将该路径添加到对应的path[i][j]中
                AddPath(path, i, j, G.vertex[j]);
            }
        }
    }
    for (k = 0; k < G.vexnum; k++) {
        for (i = 0; i < G.vexnum; i++) {
            for (j = 0; j < G.vexnum; j++) {
                if (dist[i][k] + dist[k][j] < dist[i][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                    Join(path, i, j, k);    //两个路径集合path[i][k]和path[k][j]合并
                }
            }
        }
    }
}
int main() {
    int i, j, k;
    AdjMatrix G;
    CreateDN(&G);
    ShortestPath_DJS(G);
    printf("\n");
    for (i = 0; i < G.vexnum; i++) {
        for (j = 0; j < G.vexnum; j++) {
            if (i != j) {
                printf("v%c->v%c的最短路径:", G.vertex[i], G.vertex[j]);
                if (dist[i][j] < INFINITY) {
                    for (k = 0; k <= path[i][j].last; k++)
                        printf("v%c ", path[i][j].elem[k]);
                    printf("\t路径长度为:%d", dist[i][j]);
                    printf("\n");
                }
                else
                    printf("无法到达!\n");
            }
        }
    }
    return 0;
}

好啦,关于图的应用之最短路径的两种算法就分享到这啦,我们有空的时候多想想原理,多理解理解。以后刷算法题时也是有可能遇到的。
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值