单源最短路径问题:迪杰斯特拉算法(Dijkstra’s algorithm)


前言:迪杰斯特拉算法(Dijkstra),是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题,其主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。


1、相关概念补充:

1.有权图(带权图)(Weighted Graph)
指图中的每一条边都有对应的一个或一组值,通常情况下这个值的是数值。
如:
A、B……F地之间的交通图,这里每条边的权重是两地间的行驶时间
在这里插入图片描述
与之相对:无权图👇
在这里插入图片描述
2、有向图、无向图
全部由无向边构成图称为无向图(Undirected Graph),全部由有向边构成图称为有向图(Directed Graph)
如:
在这里插入图片描述
》》》


3、邻接表(储存图的信息)
邻接表是图的一种最主要存储结构,用来描述图上的每一个点。对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。实际上我们常用的邻接矩阵就是一种未离散化每个点的边集的邻接表。
在有向图中,描述每个点向别的节点连的边(点a->点b这种情况)。
在无向图中,描述每个点所有的边(点a–点b这种情况)


4、单源最短路径
给定一个带权有向图G=(V,E),其中每条边的权是一个实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和。这个问题通常称为单源最短路径问题。
即:指定一个点(源点)到其余各个顶点的最短路径

2.(Dijkstra’s algorithm)思路讲解

问题背景:

春日,6:00,一个风和日丽、绚丽多彩的清晨,小X同学突发奇想,想到橘子洲大桥上吹一吹温柔清新的春风,根据以往的经验,他脑海里有一张大致的地图,包括地图各点之间消耗的时间。虽然小X并不是一个很爱思考的人,但他却有些好奇从学生公寓出发到橘子洲大桥的最短时间,所以,他叫醒了身为程设大神的你,请你帮他算一算。
在这里插入图片描述

》》》

思路分析:

为找出从起点到终点耗时最短的路径,你将使用
狄克斯特拉算法—>

如果使用广度优先搜索,可以找到段数最少的路径,当这不一定能确保耗时最少;可以枚举出所有可能的情况,但这时间复杂度太高
我们这里可以使用(Dijkstra’s algorithm)

Dijkstra算法步骤:

(1) 找出“最便宜”的节点,即可在最短时间内到达的节点。
(2) 更新该节点的邻居的开销,其含义将稍后介绍。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。

第一步:

找出最便宜的节点

你站在起点,不知道该前往节点A还是前往节点B。前往这两个节点都要多长时间呢?
前往节点A需要6分钟,而前往节点B需要2分钟。至于前往其他节点,你
还不知道需要多长时间。在这里插入图片描述
由于你还不知道前往终点需要多长时间,因此你假设为无穷大
节点B是最近的——2分钟就能达到。

第二步:

计算经节点B前往其各个邻居所需的时间
在这里插入图片描述
可以找到一条前往节点A的更短路径!直接前往节点A需要6分钟。
但经由节点B前往节点A只需5分钟!

进行更新:
在这里插入图片描述

——>在这里插入图片描述
对于节点B的邻居,如果找到前往它的更短路径,就更新其开销
由此:
 前往节点A的更短路径(时间从6分钟缩短到5分钟);
 前往终点的更短路径(时间从无穷大缩短到7分钟)

第三步

重复

重复第一步:找出可在最短时间内前往的节点。你对节点B执行了第二步,除节点B外,可在最短时间内前往的节点是节点A。
在这里插入图片描述
重复第二步:更新节点A的所有邻居的开销。
在这里插入图片描述
前往终点的时间为6分钟!
你对每个节点都运行了狄克斯特拉算法(无需对终点这样做)。现在,你知道:
 前往节点B需要2分钟;
 前往节点A需要5分钟;
 前往终点需要6分钟。在这里插入图片描述

最后一步

计算最终路径(找出每个节点的父节点)
最终路径如下:
在这里插入图片描述

3、(Dijkstra’s algorithm)具体实现:

在这里插入图片描述
----------------------------------》👇
在这里插入图片描述

1)将所有点与点的距离初始化为无穷大

自身与自身的距离为0

0+∞+∞+∞
+∞0+∞+∞
+∞+∞0+∞
+∞+∞+∞0

代码如下:

const int INF = 0x3f3f3f3f;  //为无穷大,16进制;
int D[2005][2005];//创建邻接表,描述图
int N  ;//N为点的个数;
        for( i = 1 ;i <= N ;i++)
        {
            for( j = 1 ;j <= N ;j++)
            {
                if(i==j)D[i][j]=0;
                else D[i][j] = INF;
            }
        }

或者:

#include<string.h>//memset函数头文件
const int INF=0x3f3f3f3f;
int D[2005][2005];
int N;
memset(D,INF,sizeof(D));
for(int i=1;i<=N;i++)D[i][i]=0;
2)导入具体数据
int T;//边的个数
int x,y;//同一边的两个点
int tmp;//tmp为每个边的全职
while(T--){
    scanf("%d%d%d",&x,&y,&tmp);
    if(D[x][y]>tmp){
         D[x][y]=tmp;//无向图,D[x][y]表示从x到y处
         D[y][x]=tmp;
    }
}

另外,创建数组,储存从起点①到各点的最短距离(初始)

int d[2005];     //用来记录“当前最短距离”;
d[1]=0;
for(int i=2;i<=N;i++)d[i]=INF;
026+∞
3)Dijkstra算法-》

导入数据后的邻接表如下

026+∞
2035
6301
+∞510

d[1]=0,d[2]=2,d[3]=6,d[4]=INF

1️⃣-》

从①点的最短距离,先从①点出发,找到距离①最短的点。即②

在这里插入图片描述

现在位置来到②处,这里有②->③,和②->④
①->②->③的距离为5,①->③的距离为6,更新d[3]=5
①->②->④的距离为7,①->④的距离为INF,更新d[4]=7.

2️⃣-》

》从①点的最短距离,先从①点出发,找到距离①最短的点(已标记的点②跳过),那么目前距离①最短的点为③
③的出处有④(①和②均被标记)
d[3]=5,D[3][4]=1,所以①->②->③->④的距离为6,小于目前的d[4](①->②->④),所以d[4]=6.
在这里插入图片描述

3️⃣-》

所有点均已标记。(终点除外)
全部找完,确定了A到各个点的最短路径。


4、例题:Til the Cows Come Home

POJ2387传送门

Language:
Default
Til the Cows Come Home
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 123242 Accepted: 39112
Description:
Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before Farmer John wakes her for the morning milking. Bessie needs her beauty sleep, so she wants to get back as quickly as possible.

Farmer John’s field has N (2 <= N <= 1000) landmarks in it, uniquely numbered 1…N. Landmark 1 is the barn; the apple tree grove in which Bessie stands all day is landmark N. Cows travel in the field using T (1 <= T <= 2000) bidirectional cow-trails of various lengths between the landmarks. Bessie is not confident of her navigation ability, so she always stays on a trail from its start to its end once she starts it.

Given the trails between the landmarks, determine the minimum distance Bessie must walk to get back to the barn. It is guaranteed that some such route exists.

Input

  • Line 1: Two integers: T and N

  • Lines 2…T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the trail travels. The third integer is the length of the trail, range 1…100.

Output:

  • Line 1: A single integer, the minimum distance that Bessie must travel to get from landmark N to landmark 1.

-Sample Input

5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100

Sample Output

90

HINT:

INPUT DETAILS:

There are five landmarks.

OUTPUT DETAILS:

Bessie can get home by following trails 4, 3, 2, and 1.

题目大意:

定N个地点(编号从1到N),T条道路,先输入两个正整数T和N,接下来T行,每行三个整数,分别代表这条道路的起点和终点和长度,要求求出从N到1的最短路径;

解答:

解1:(普通的Dijkstra算法)

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int inf=0x3f3f3f3f;
int T,N,x,y,tmp,the_min,flag;
int D[2005][2005];//输入的数据,各点间距离
bool visited[2005]={0};
int d[2005];
int main(){
while(scanf("%d%d",&T,&N)!=EOF){  //注意是多组数据
for(int i=1;i<=N;i++){
    for(int j=1;j<=N;j++){
        D[i][j]=inf;
    }
}
for(int i=1;i<=N;i++)D[i][i]=0;
while(T--){
    cin>>x>>y>>tmp;
    if(D[x][y]>tmp){//注意,这个if不能省去
    D[x][y]=tmp;
    D[y][x]=tmp;
    }
}
memset(d,inf,sizeof(d));
d[1]=0;
for(int i=1;i<=N;i++){
    the_min=inf;
    for(int j=1;j<=N;j++){
        if(the_min>d[j]){
            if(!visited[j]){
                the_min=d[j];
                flag=j;
            }
        }
    }
    for(int k=1;k<=N;k++){
        if(d[k]>d[flag]+D[flag][k])d[k]=d[flag]+D[flag][k];
    }
    visited[flag]=1;
}
cout<<d[N]<<endl;
for(int i=1;i<=N;i++)visited[i]=0;
}
return 0;
}

解法二:(运用优先队列优化)
原文地址

#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std ;
const int INF = 0x3f3f3f3f;
int G[2000][2000];
int d[2000];
int i ,j;
struct node{
    int num;              //记录结点的序号;
    int dis;            //记录结点的“最短距离”
    friend bool operator<(node a ,node b)
    {
        return a.dis>b.dis;//运算符重载,优先队列原本是从大到小输出,现在改写后变成从小到大;
    }
};
int main()
{
    int M , N;
    int x,y,D;
    priority_queue<node>que;//将等下的“最短路径”入队;
    while(scanf("%d%d",&M,&N)!=EOF)
    {
        for( i = 1 ;i <= N ;i++)
        {
            for( j = 1 ;j <= N ;j++)
            {
                G[i][j] = INF;//先全部初始化为无穷大;
            }
        }
            for(int i  = 1 ; i <= N ;i++)
        {
            G[i][i] = 0;
        }
        for( i = 1; i <= M ;i++)
        {
            scanf("%d%d%d",&x,&y,&D); //输入数据;
                if(G[x][y]>D)//如果比无穷大小就更新距离;即有数据的就有数据,无直接连通就是无穷大;
                {
                    G[x][y] = D;
                    G[y][x] = D;
                }
        }
        memset(d,0x3f,sizeof(d));
        d[1] = 0;
        que.push({1,0}); //将起点入队,起点和起点的距离是为0,因为是本身;
        while(!que.empty())
        {
            node tp = que.top(); //每次取出队列最前的数,即最小的数;
            que.pop();
            for(i = 1 ;i <= N ;i++)
            {
                if(G[tp.num][i])//如果两点间有距离
                {
                    if(d[i]>d[tp.num]+G[tp.num][i])//每次更新“当前最短距离”
                    {
                        d[i] = d[tp.num] + G[tp.num][i];
                        que.push({i,d[i]});//入队;
                    }
                }
            }
        }
        printf("%d\n",d[N]); //直接输出1到N的最短距离;
        while(!que.empty())  //注意队列要清空;
        {
            que.pop();
        }
    }
    return 0;
}
  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0<Solving)1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值