Dijkstra算法


前言

****在这里插入图片描述
首先我们来看看什么叫是最短路。看上图,顶点代表不同的地方,边的距离代表不同地方之间的距离。假如小屈住在1号顶点,小孙住在5号顶点,小屈想要找到小孙玩耍,小屈可以直接找小孙 1 -> 5需要走10公里,但是小屈如果聪明一些,会选择绕道2号顶点,再去小孙家,1 -> 2 -> 5,一共需要走9公里。这就算一种最短路:)

一、Dijkstra简介

该算法由Edsger Wybe Dijkstra于1959年提出。是求一个点(源点)到其余各顶点的最短路径,也叫“单源最短路径”。Dijkstra算法有一个局限性,无法处理由负权边的图。但还是很有用的,因为现实问题很少有出现负权边。比如你买车票,人家总不能倒贴你钱吧~。重重之重: Dijkstra处理的图一定不能出现负权边,这一点很重要要牢记!!!

二、算法实现

1.算法具体思想:

1.首先我们定义 dis[i] 数组,表示源点到其余顶点的最短距离。拿上图来举例,我们假设源点为 1 号顶点,dis[ 2 ]就表示源点(1号顶点)到 2 号顶点的最短距离。初始化 dis[i] 数组, dis[1] = 0,因为源点到自己的距离就是0嘛; 其余位置初始化为无穷大,因为你现在还不知道去其余顶点的 “ 路 ”。多提一下, dis[1] = 0,此时我们把它标记为已经求出最短路的顶点。你不可能找到比0还短的路了。
2.然后遍历图,然后遍历 1 号顶点(最小值点), 发现分别有两条边 1 -> 2和 1 -> 5,这两条边比我们在dis数组里面记录的距离 (无穷大)要小,所以我们更新dis数组的值。dis[2] = 2 和 dis[ 5 ] = 9。(更新dis数组)
3.然后, 遍历 dis 数组找到值最小的顶点, 当前最小的就是 2号顶点,通过上一轮操作就已经求出了 源点 到 2 号顶点的最短路!把 2 号顶点标记为已经求出最短路的结点。为什么说到 2号顶点的最短路已经求出了 ? 因为你不可能通过其他顶点来缩短 源点到2号顶点的距离! 因为所有边的权值都是正值!因为该图不存在负权边!如果通过其他边,意味着距离的增加! 这里如果读者不明白,可以画图模拟模拟:)
4.通过 n 轮上述操作就可以求出 “ 单源最短路 ”, 请看下面的详细代码(可以帮助理解~

2.算法代码实现:

//读代码建议 :先看变量的定义,在看main()函数:)
//基于贪心算法, 输出的是顶点1 到顶点n 的最短距离, O(n*n)
#include<iostream>
#include<cstring>
using namespace std;

const int N = 100;
//记录点数和边数
int n, m;
//临接矩阵 -- 存储图
int g[N][N];
//记录 dis数组 顶点 1 到顶点 n 的最短距离
int dis[N];
//记录该顶点是否已经求出最短路了 ? 
bool st[N];

void dijkstra() {
    // 初始化dist数组, 每一个值被初始化为 0x3f3f3f3f, 把0x3f3f3f3f当作无穷大
    memset(dis, 0x3f, sizeof(dis));
	
	// 因为 1 是源点,所以 初始化0
    dis[1] = 0;

    //dijkstra算法核心
    //通过n次遍历 , 把n个顶点的加入到最短路集合
    for (int i = 0; i < n; i++) {

        //TODO::找到距离源点距离最短的点
         
        //t保存的是顶点号
        int t = -1; 
        for (int j = 1; j <= n; j++)
            if (!st[j] && (t == -1 || dis[t] > dis[j])) // 该结点必须是为求出最短路, 求最小值
                t = j;

        //TODO::加入最短路集合,标记已经求出 源点 到 结点t 最短路了
        st[t] = true;

        //TODO::通过该顶点更新dis数组,遍历dis数组
        
        for (int j = 1; j <= n; j++)
            if (dis[j] > dis[t] + g[t][j])
                //如果源点到j的距离 小于 源点到t的距离 + t 到 j 的距离。则进行更新
                //就像是换条路来试试 到达目的地的距离能否更短, 这个操作也被称为 “松弛操作”
                dis[j] = dis[t] + g[t][j];
    }

}


int main() {
    //读入点数 和 边数
    cin >> n >> m;

    //初始化地图, 地图的每一个值被初始化为 0x3f3f3f3f
    memset(g, 0x3f, sizeof(g)); 

    //读入边
     while(m --) {
         int a, b, c; 
         // 顶点a 到 顶点b 的距离是 c;
         cin >> a >> b >> c;
         //这里使用min,是图可能存在重边, 选择最小边保存就好
         g[a][b] = min(g[a][b], c); 
     }

    //调用dijkstra算法
    dijkstra();

    //输出结果
    cout << "到每个顶点的最短距离  :";
     for(int i = 1; i <= n; i++)
         cout << dis[i] << ends;
     cout << endl;

    system("pause");
    return 0;
}
/*
输入样例
5 8
1 5 10
1 2 2
2 5 7
2 3 3
3 1 4
3 4 4
4 5 5
5 3 3
输出样例
0 2 5 9 9
*/

可以使用输出样例 和 输入样例 来测试代码~


总结一下

Dijkstra是一种基于贪心策略的算法。众所周知 "贪心的证明 "是很麻烦的~~ ,我也知识口述了一些贪心的思想, 帮助理解
我发出来的基础班Dijkstra算法复杂度为O( N * N )。很明显,求最小值的那一步可以使用堆来优化为O( m*log n ),这就交给各位读者了。
本人写博客的目的也只是为了加深对算法的印象,如果可以帮到大家就更好了。有错误,还请大家指正~

参考资料 《啊哈算法》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十七溯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值