迪杰斯特拉算法详解

1.定义

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

*以上摘自百度百科

要知道的点是:所有边上的权值必须是非负值,否则不能用这个算法。

2.逻辑

1.我们先默认所有点之间都没有联系,所以长度设置为无穷大。

2.输入起点from和终点to

3.把起点的可达点加入队列

4.遍历这些点的距离,挑选出权值最短的,把它前往的那个点状态设置为访问过。

5.算出其它方案到这个点是否比前一个方案耗费权重更少,如果更少就更新权重。

6.重复3~5步,直到全部的点都访问过。

3.实现方法

先上个图!(大家可以根据这个图来自己根据逻辑模拟一遍,这样就更能理解下面的代码)

 就用这张熟悉的G1来作为示范吧。(测试数据会用到)

首先,我们还是需要设置一个“无穷大”的值:

#define INF 0x3f3f3f
//或者如下
const int INF=0x3f3f3f;

然后我们使用数组来存储起点到每个点的最短距离,用bool数组存储是否访问过,并使用二维数组来存储邻接矩阵:

const int maxn=1001;//设置一个常量最大值
bool vis[maxn];//访问过?
int dis[maxn];//最短距离
int adj[maxn][maxn];//邻接矩阵

之后,再定义点数、边数、起点和终点的变量即可:

int n,m;//点、边的数量
int from,to;//起终点

好了,变量定义的部分我们就完成了。接下来看一下主函数部分:

(这个主函数很简单,所以直接在代码里加注释加以说明):

int main()
{
    cin>>n>>m;//点的数量和边得数量
    for(int i=0;i<=m;i++)
    {
        int u,v,w;//两个顶点和权值
        adj[u][v]=adj[v][w]=w;//邻接矩阵
    }
    cin>>from>>to;//起终点
    dijkstra();//迪杰斯特拉算法
    cout<<dis[to];//输出
    return 0;//结束程序
}

好了,到最核心的部分了,也就是迪杰斯特拉算法函数内部。

首先,我们先定义两个变量:minn表示从起点开始最小的权值,pos表示那个minn所对应的那个边权值最小的点的下标。

int minn;//起点开始最小的权值
int pos;//所对应的那个边权值最小的点的下标

之后进行初始化:先全部设置为没有被访问过,把距离通过邻接矩阵更新起点开始的距离,并把起点设置为访问过。

memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++){
	dis[i]=adj[from][i];
}
vis[from]=true;

接下来就是把n个顶点所要的权值进行比较,最小的权值设置为minn(每次循环都要初始化为无穷大),角标设置为pos,每次更新最小值:

注:以下本函数内部的操作均在这个for(int i=1;i<=n;i++)这个循环内部进行操作。

for(int i=1;i<=n;i++)
    {
        minn=INF;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]<minn)
            {
                minn=dis[j];
                pos=j;
            }
        }
    }

筛选完后,把该点设置为访问过。

vis[minn]=true;

接下来就是循环更新最短路径了。

对于没有被访问过的点,如果发现有别的方法去这个点的路径权重比原来的路径小,就更新起点到该点的最短距离。

for(int j=1;j<=n;j++)
{
    if(!vis[j]&&dis[j]>dis[pos]+adj[pos][j])
    {
        dis[j]=dis[pos]+adj[pos][j];
    }
}

到了这里,整个迪杰斯特拉算法的函数也就算完成了,只需把这个函数放在程序中即可。

还是老套路,献上完整代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f//“无穷大”的值
//或 const int INF=0x3f3f3f;
const int maxn=1001;//设置一个常量最大值
bool vis[maxn];//访问过?
int dis[maxn];//最短距离
int adj[maxn][maxn];//邻接矩阵
int n,m;//点和边的数量
int from,to;//起终点
void dijkstra();//迪杰斯特拉算法函数
int main()
{
    cin>>n>>m;//点的数量和边的数量
    for(int i=0;i<m;i++)
    {
        int u,v,w;//两个顶点和权值
        cin>>u>>v>>w;
        adj[u][v]=adj[v][w]=w;//邻接矩阵
    }
    cin>>from>>to;//起终点
    dijkstra();//迪杰斯特拉算法
    cout<<dis[to];//输出结果
    return 0;//结束程序
}

void dijkstra()
{
    int minn;//最小权重值
    int pos;//最小权重的角标
    memset(vis,false,sizeof(vis));//初始化为没有访问过
    for(int i=1;i<=n;i++)//初始化
    {
		dis[i]=adj[from][i];
	}
	vis[from]=true;//起点设为访问过
	for(int i=1;i<=n;i++)//找最小权重部分
    {
        minn=INF;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]<minn)
            {
                minn=dis[j];
                pos=j;
            }
        }
        vis[minn]=true;//设为访问过
        for(int j=1;j<=n;j++)//在没有访问过里面更新最短路径
        {
            if(!vis[j]&&dis[j]>dis[pos]+adj[pos][j])
            {
                dis[j]=dis[pos]+adj[pos][j];
            }
        }
    }
}

G1测试数据 :

4 5

1 2 1
1 3 4
1 4 8
2 3 6
2 4 4

1 4

输出结果:

 结束~

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值