最短路径-迪杰特斯拉算法(1)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43536100/article/details/86744914

Dijkstra’s algorithm

最近几天闲来一事无成,转念一想此不是长久之计,于是垂死病中惊作起,写下两日间,个人对迪杰特斯拉算法的见解

介绍

由于在刷题中遇到了求最短路线的题目,没有任何思路。于是求助与百度,了解到了迪杰特斯拉早在许久以前就提出了该算法来求解此类问题。
Dijkstra’s algorithm无法解决有负权重的图。

算法简介

图来自 Wikipedia

Dijkstra's algorithm

迪杰特斯拉算法是知道源点进而算到其他每个结点的最短路径的算法
但是如果仅仅知道每个结点的最小值却不是我们想要的,我们还要知道到每个从源点到该结点走的路径。至于为什么还需要保存每个到该结点的路径,这个疑问留到算法手工分解来阐释。
当然如果我们只需要知道最短数量的话,我们可以不用保存路径。

手工分解算法

say

我们假设起源点为V0

Part 1

由V0开始我们可以发现有三条通路分别指向V5,V4,V2,比较三者的数值大小我们可以知道V2的数值为最小的,由于我们不知道此时的路径是否是最短,所以我们定义了两个数组shortest[index] = MinLon,MinLon是确认到目前到该点最短路径。

当我们分析完V0点的时候我们得知了以下的信息

shortest[index] MinLon
V2 10
V4 30
V5 100

Part 2

基于Part 1

我们知道了V2点的最短路径。

这里会有疑问,可能有人会认为绕一圈后到V2点的路径可能会更短,但这是 不可能的,首先我们得知V2是从源点出发一步就能到达的点,而且是最短的那个,如果我们走V2,而从别的点开始绕圈的话,首先第一步就已经比V2大了,所以绕圈之后的路径长度再次到达V2的路径长度一定会比从源点到V2的大。

这里有一个必要条件是V2是离源点最近的点

第二步我们就由shortest[index]的排序来进行。

第一位的是V2,我们发现V2只有一条通路指向V3,由此我们可以得到,到目前为止shortest[V3] = 60。

第二个行走的是V4,V4有两条通路指向V5和V3,计算得到从V4->V5的路径量为90 小于前面保存的100,因此更新shortest[V5] = 90,V4->V3 为 50 小于前面的 60 所以 shortest[V3] = 50。

第三个行走的为V5,由于V5没有通路,所以结束 。

当我们分析完以上,我们得到以下信息

shortest[index] MinLon
V3 50
V5 90

Part 3

基于Part 2

类仿Part 2

第一位是V3,V3只有一条通路指向V5,V3->V5 为 60 小于之前的 90 ,shortest[V5] = 60。

第二位是V5,V5没有通路结束。

我们得到以下数据

shortest[index] MinLon
V5 60

Part 3

由于V5 没有通路 所以算法结束。

另外一种结束情况是,已知所有结点的所有通路的结点皆为最小 /由目前推测的下一目标无法更小时/

总结

由上分析可得

shortest[index] MinLon
V0 0
V1 INT_MAX
V2 10
V3 50
V4 30
V5 60

PseudoCode

Dijkstra's algorithms
1. 初始化所有shortest[n+1]为无穷大,即为INT_MAX
2. 定义一个数组queque[m]来保存每次搜索的到的值
3. 定义两个指针p,r//p指向需要搜索的index,r用来扩容
4. while(p < r)
	1. 用p指向的数据进行搜索
	2. 加上这次搜索的边距离观察是否为该结点的最短距离
		1. 如果是就改变为当前值,并将此次改变的下表记录到Tmp数组中
		2. 反之则不改变
	3. 对Tmp数组进行排序(范围限定)(依靠Tmp里面保存的下标值,找到shortest[Tmp[i]]来排序)
	4. 用 r 来进行扩容
5. 结束得到min值

代码实现

#include <stdio.h>
#include <limits.h>

#define N 6
#define long long int int64
#define M 10000
#define prtmin(x) printf ("%d  = %d\n",i,x);

int main()
{
    int shortest[6];
    int queque[M];
    int *p = queque;
    int *r = p + 1;
    int Tmp[6] = {0};
    int way[6][6] = {0};
    for(int i=0; i<6; i++)
        for(int j=0; j<6; j++)
            scanf("%d",&way[i][j]);
    // 1
    for(int i=0; i<6; i++) shortest[i] = INT_MAX;
    //2 
    *p = 0;
    shortest[0] = 0;
    //3
    while(p < r)
    {
        int cont=0;
		for(int i=0; i<N; i++)
        {
            int key = way[*p][i] + shortest[*p];
            if(key < shortest[i])
            {
                shortest[i] = key;
                Tmp[cont++] = i;
            }
        }
        for(int i=0; i<cont; i++)
        	for(int j=0; j<cont-i-1; j++)
                if(shortest[Tmp[j]] > shortest[Tmp[j+1]])
                {
                    int Cup = Tmp[j];
                    Tmp[j] = Tmp[j+1];
                    Tmp[j+1] = Cup;
                }
        for(int i=0; i<cont; i++) *(r++) = Tmp[i]; //扩容操作 可能会越界
        p++;
    }
    for(int i=0; i<N; i++) prtmin(shortest[i]);
    return 0;
}

缺点

里面的p和r指针可能越界,并且每次冒泡排序当数据过大时候无法发挥优势。

展开阅读全文

没有更多推荐了,返回首页