最短路径问题-Dijkstra算法

有权单源最短路径问题

具体思路和图解看这个-https://www.cnblogs.com/ssyfj/p/9491895.html

知识点:

1)权值不可为负

2)Dijkstra算法解决了从某个顶点到其余各顶点的最短路径。其时间复杂度是O(n*2)

更详细的图解(一看就懂!!!):https://zhuanlan.zhihu.com/p/129373740

实例:https://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html

一个无向图

 

用Dijkstra算法找出以A为起点的单源最短路径步骤如下:

 

一:有权的单源最短路径算法(迪杰斯特拉Dijkstra算法)

复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "queue.h"

#define MAXVEX 100    //最大顶点数
#define INFINITY 65535    //用0表示∞

typedef char VertexType;    //顶点类型,字符型A,B,C,D...
typedef int EdgeType;    //边上权值类型10,15,...

//邻接矩阵结构
typedef struct
{
    VertexType vers[MAXVEX];    //顶点表
    EdgeType arc[MAXVEX][MAXVEX];    //邻接矩阵,可看作边表
    int numVertexes, numEdges;    //图中当前的顶点数和边数
}MGraph;


//创建邻接矩阵
void CreateMGraph(MGraph* G);
//显示邻接矩阵
void showGraph(MGraph G);
//迪卡斯特拉算法,获取最短路径
void Dijkatra(MGraph G, int s);

void Dijkatra(MGraph G,int s)
{
    int path[MAXVEX];    //是数组下标表示的顶点所经历的前一个顶点
    int dist[MAXVEX];    //是数组下标表示的顶点的最小权值路径和
    //上面两个数组都有作用,和无权最短路径一致,但是无权最短路径可以使用dist是否被设置来判断一个顶点是否被访问,
    //但是这里无法使用,因为dist和普里姆算法中的lowcost一样,是使用贪心算法时,每到一个顶点,我们都会全部更新dist
    //所以我们需要另外一个数组来标志各个顶点是否被访问
    int final[MAXVEX];
    int i,j,k,min;

    //对数据进行初始化
    for (i = 0; i < G.numVertexes;i++)
    {
        final[i] = 0;    //0表示该数组下标所表示的顶点未被访问
        path[i] = 0;    //初始化路径数组为0,表示当前每个都是独立的根节点
        dist[i] = G.arc[s][i];    //这一步是重点:初始化路径数组的值为起始v0到各个点的权值
    }
    dist[s] = 0;    //到源点自己的路径为0
    path[s] = s;    //设置源点的前一个顶点就是自己
    final[s] = 1;    //源点被访问过了

    //开始主循环,每次求的v0(s)到某个v顶点的最短路径
    for (i = 0; i < G.numVertexes;i++)
    {
        min = INFINITY;    //和普里姆算法相似
        for (j = 0; j < G.numVertexes;j++)    //由于是有向图所以都要从0开始,找到他的每个邻接点
        {
            if (!final[j]&&dist[j]<min)    //若是该顶点没有被访问过,且该点到s点的距离小于min,我们就将min设置为他
            {
                k = j;    //记录下该v到s点的下标和min最小路径
                min = dist[j];
            }
        }

        final[k] = 1;    //将目前找到的距离v0(S)最近的顶点置为1

        for (j = 0; j < G.numVertexes;j++)    //修正当前最短路径即距离
        {
            //修正方法就是循环k的每个邻接点,我们作为三角形来看,若是两边之和小于第三边,那我们原来找的那条直接的最短边就失效了,用这两条直接代替
            //所以我们将距离修改,路径设置为他的上一步k,
            if (!final[j]&&(min+G.arc[k][j])<dist[j])
            {
                //说明找到了更短的路径,修改dist和path数组
                dist[j] = min + G.arc[k][j];    //修改当前路径长度
                path[j] = k;
            }
        }
    }

    for (i = 0; i<G.numVertexes; i++)
    {
        printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]);
    }
}

int main()
{
    MGraph MG;
    CreateMGraph(&MG);
    showGraph(MG);
    Dijkatra(MG,0);
    system("pause");
    return 0;
}

//生成邻接矩阵
void CreateMGraph(MGraph* G)
{
    int i, j, k, w;
    G->numVertexes = 7;
    G->numEdges = 12;
    //读入顶点信息
    G->vers[0] = 'A';
    G->vers[1] = 'B';
    G->vers[2] = 'C';
    G->vers[3] = 'D';
    G->vers[4] = 'E';
    G->vers[5] = 'F';
    G->vers[6] = 'G';
    G->vers[7] = 'H';
    G->vers[8] = 'I';

    //getchar();    //可以获取回车符
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = INFINITY;    //邻接矩阵初始化

    //创建了有向邻接矩阵
    G->arc[0][1] = 2;
    G->arc[0][3] = 1;
    G->arc[1][3] = 3;
    G->arc[1][4] = 10;
    G->arc[2][0] = 4;
    G->arc[2][5] = 5;
    G->arc[3][2] = 2;
    G->arc[3][4] = 2;
    G->arc[3][5] = 8;
    G->arc[3][6] = 4;
    G->arc[4][6] = 6;
    G->arc[6][5] = 1;
}


//显示邻接矩阵
void showGraph(MGraph G)
{
    for (int i = 0; i < G.numVertexes; i++)
    {
        for (int j = 0; j < G.numVertexes; j++)
        {
            if (G.arc[i][j] != INFINITY)
                printf("%5d", G.arc[i][j]);
            else
                printf("    0");
        }
        printf("\n");
    }
}
复制代码
复制代码
记忆方法:
假设10个节点
G【】【】:
       0 1 2 3 4 5 
       A B C D E F
0   A  0 6 3 9 9 9
1   B  6 0 2 5 9 9
2   C  3 2 0 3 4 9
3   D  9 5 3 0 2 3
4   E  9 9 4 2 0 5
5   F  9 9 9 3 5 0
int dijkstra(Graph G)
{
1:需要一个记录最短路径数值的数组dist【10】,一个记录是否访问节点的数组final【10】,
一个记录最短路径中当前节点前一个节点地址的数组path【10】; 2:dist【10】数组赋值-将G【0】赋值给dist【10for(i=0;i<10;i++) { dist[i]=G[0][i]; path[i]=0; final[i]=0; } 3:第一个节点不需要循环先写好数据dist[0]=0;final[0]=1; path[0]=0; 4:核心步骤 for(i=0;i<10;i++) { min=999;//假设为无穷 for(j=0;j<10;j++) { if(final[j]!=1&&dist[j]<min) { min=dist[j]; min_index=j; } } final[min_index]=1; for(j=0;j<10;j++) { int p=dist[min_index]+G[min_index][j]; if(final[j]!=1&&dist[j]>p) { dist[j]=p; path[j]=min_index; } } }
复制代码

二:基于无向图的顶点加权Dijkstra算法

头文件及及结构

复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAXVEX 100
#define INFINITY 65535 

typedef char VertexType;
typedef int EdgeType;

typedef struct  
{
    VertexType vers[MAXVEX]; //顶点表
    EdgeType arc[MAXVEX][MAXVEX]; //邻接矩阵
    int numVertexes, numEdges;    //图中的顶点数和边数
}MGraph;

//创建邻接矩阵
void CreateMGraph(MGraph* G);
//显示邻接矩阵
void showGraph(MGraph G);
//迪杰斯特拉算法,获取最短路径
void Dijkatra(MGraph G, int s);
复制代码

1)CreateMGraph(MGraph* G)

复制代码
void CreateMGraph(MGraph* G)
{
    int i, j;
    G->numVertexes = 7;
    G->numEdges = 12;
    //读入顶点信息
    G->vers[0] = 'A';
    G->vers[1] = 'B';
    G->vers[2] = 'C';
    G->vers[3] = 'D';
    G->vers[4] = 'E';
    G->vers[5] = 'F';
    G->vers[6] = 'G';
    G->vers[7] = 'H';
    G->vers[8] = 'I';

    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = INFINITY;    //邻接矩阵初始化

    //创建了有向邻接矩阵
    G->arc[0][1] = G->arc[1][0] = 2;
    G->arc[0][3] = G->arc[3][0] = 1;
    G->arc[1][3] = G->arc[3][1] = 3;
    G->arc[1][4] = G->arc[4][1] = 5;
    G->arc[2][0] = G->arc[0][2] = 4;
    G->arc[2][5] = G->arc[5][2] = 5;
    G->arc[3][2] = G->arc[2][3] = 2;
    G->arc[3][4] = G->arc[4][3] = 2;
    G->arc[3][5] = G->arc[5][3] = 8;
    G->arc[3][6] = G->arc[6][3] = 4;
    G->arc[4][6] = G->arc[6][4] = 2;
    G->arc[6][5] = G->arc[5][6] = 3;
    //读入顶点信息
    for (i = 0; i < G->numVertexes; i++)
        G->arc[i][i] = 0;
    G->arc[1][1] = 4;
    G->arc[2][2] = 2;
    G->arc[3][3] = 10;
    G->arc[4][4] = 1;
    G->arc[5][5] = 3;
}
复制代码

2)showGraph(MGraph G)

复制代码
void showGraph(MGraph G)
{
    for (int i = 0; i < G.numVertexes; i++)
    {
        for (int j = 0; j < G.numVertexes; j++)
            printf("%6d", G.arc[i][j]);
        printf("\n");
    }
}
复制代码

3)Dijkatra(MGraph G, int s)

复制代码
void Dijkatra(MGraph G, int s)
{
    int path[MAXVEX];    //是数组下标表示的顶点所经历的前一个顶点
    int dist[MAXVEX];    //是数组下标表示的顶点的最小权值路径和
    //上面两个数组都有作用,和无权最短路径一致,但是无权最短路径可以使用dist是否被设置来判断一个顶点是否被访问,
    //但是这里无法使用,因为dist和普里姆算法中的lowcost一样,是使用贪心算法时,每到一个顶点,我们都会全部更新dist
    //所以我们需要另外一个数组来标志各个顶点是否被访问
    int final[MAXVEX];
    int i, j, k, min;

    //对数据进行初始化
    for (i = 0; i < G.numVertexes; i++)
    {
        final[i] = 0;    //0表示该数组下标所表示的顶点未被访问
        path[i] = 0;    //初始化路径数组为0,表示当前每个都是独立的根节点
        dist[i] = G.arc[s][i];    //这一步是重点:初始化路径数组的值为起始v0到各个点的权值
    }
    dist[s] = 0;    //到源点自己的路径为0
    path[s] = s;    //设置源点的前一个顶点就是自己
    final[s] = 1;    //源点被访问过了

    //开始主循环,每次求的v0(s)到某个v顶点的最短路径----(找到距离源点s,并且没有被访问过的顶点的最近顶点)
    for (i = 0; i < G.numVertexes; i++)
    {
        min = INFINITY;    //和普里姆算法相似
        for (j = 0; j < G.numVertexes; j++)    //由于是有向图所以都要从0开始,找到他的每个邻接点
        {
            if (!final[j] && dist[j] < min)    //若是该顶点没有被访问过,且该点到s点的距离小于min,我们就将min设置为他
            {
                k = j;    //记录下该v到s点的下标和min最小路径
                min = dist[j];
            }
        }

        final[k] = 1;    //将目前找到的距离v0(S)最近的顶点置为1

        for (j = 0; j < G.numVertexes; j++)    //修正当前最短路径即距离
        {
            //修正方法就是循环k的每个邻接点,我们作为三角形来看,若是两边之和小于第三边,那我们原来找的那条直接的最短边就失效了,用这两条直接代替
            //所以我们将距离修改,路径设置为他的上一步k,
            if (!final[j] &&k!=j&&(min + G.arc[k][j]+G.arc[k][k]) < dist[j]) //开始加上了经过的顶点权值进行比较
            {
                //说明找到了更短的路径,修改dist和path数组
                dist[j] = min + G.arc[k][j] + G.arc[k][k];    //修改当前路径长度,是加上途经的顶点权值
                path[j] = k;
            }
        }
    }

    for (i = 0; i < G.numVertexes; i++)
    {
        printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]);
    }
}
复制代码

4)

复制代码
int main()
{
    MGraph MG;
    CreateMGraph(&MG);
    showGraph(MG);
    Dijkatra(MG, 0);
    system("pause");
    return 0;
}
复制代码

 

posted on   Y-flower  阅读(219)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示