Dijkstra algorithm原理及使用

    Dijkstra algorithm(迪杰斯特拉算法)是用来求单源最短路径的,确定哪个是源点之后,可以得到从源点到其余所有顶点之间的最短路径和最短距离(最短路径是指经过哪些结点,最短距离是指最短路径经过的距离)。它适用于非负权值的带权图。

1. Dijkstra algorithm的原理

    首先我们来看三个顶点,如何进行单源最短路径规划:

《Dijkstra algorithm原理及使用》

    我们再来看四个顶点,如何进行单源最短路径规划:

《Dijkstra algorithm原理及使用》

    我们从以上过程得到规律

    假如我们用G=(V,E)来表示我们的图。我们要准备一个集合S,来表示已经规划好的最短路径内的顶点,那么V-S就是还没规划好的顶点。还需要一个向量dist,用来存放点点之间的距离,和一个向量path,来存放经过的顶点号,往后可以用于解读得到最短路径是什么。

    step1:初始化dist和path,dist为点点距离,如果不连通则为nan;path为经过节点号(matlab的节点号是从1开始),自己到自己为-1,不连通也为-1。

    step2:对S中任意一个点,得到它和V-S中任意一个点的距离,这里的距离不是一开始的距离,而是dist中对应的值(dist中的值可以由step2进行更新),从中选择距离最小的值,这就是可以确定的最短路径,并且记录下,这个最短路径的末节点号设为min,将min放入S集合。如果道路不通,就进入step4;(这一阶段可以叫做最短路径判断)

    step3:根据step1中的min作为中间节点,去寻找V-S中的所有节点,设为k,去看是否dist(min)+min到k的距离<dist(k)(dist(k)就是源点直接到k的距离),如果满足,则将dist(k)进行更新,值更新为dist(min)+min到k的距离,并且path(k)记录下min。(这一阶段可以叫做顶点间距离更新)

   step4:进入step1,重复定点数-1次

    基于这样的算法,我们得到了下面的程序。


    另外,还有一个要讲的,就是关于path的解读。这也涉及到了,下面的接口的最后一段解读过程的原始思想:

    对于上面的四个顶点的情况,假如出现这样的情况(后面的几个数据是我瞎写的),path(3)=1,path(2)=3,path(4)=2,那么我们怎么解读,首先我们知道path(1)=-1的,这是真的,算法step定义好了的。那么,A到C也就是path(3)=1,‘1’说明经过的节点是1,1就是A,所以A到C的最短路径就是AC。那么再来看path(2)=3,A到B之间经过了3也就是C,但是3由path(3)=1可以得到又经过了节点A,所以A到B最短路径ACB。那么这样的方法看path(4)=2,A到D经过‘2’也就是B,由前面可以得到,A到B又经过C,A到C只经过A自己,所以最短路径ACBD。

    把这一过程总结一下,就是一个解读path的算法:

先建一个向量存放经过的节点,each_path = [];

对于第i个path(i),i’=path(i);

Repeat

{

把i‘放进each_path;

i’=path(i’);

}直到i’=-1

把each_path逆序遍历就可以得到从源点到i号顶点的最短路径经过的所有顶点号


2. Dijkstra algorithm的使用

    对于一个这样的图:

《Dijkstra algorithm原理及使用》

    求它的单源最短路径。

    关于Dijkstra algorithm的使用,提供给大家两个自己开发的接口,写了很长时间啊,第一个接口取名dijkstra_algo(),新建一个m-file,取名dijkstra_algo.m,内容如下:

————————————————————————————

% 任务目标:开发Dijkstra算法的接口,输入是源点号index_i、邻接矩阵mat,输出是
% 最短距离向量dist、最短路径阵向量path,以及命令行输出详细规划
function [dist, path] = dijkstra_algo(index_i, mat)
max_weight = 65536;
n = size(mat,2);
% s用来记录已存的顶点,为1表示已存进s集合
s = zeros(1,n);
s(index_i) = 1;
% 初始化dist和path向量,向量运算复杂度较矩阵运算低
dist = zeros(1,n);
path = zeros(1,n);
for i = 1:n,
    dist(i) = mat(index_i,i);
    if ~isnan(dist(i)) && i~=index_i,
        path(i) = index_i;
    else
        path(i) = -1;
    end
end

for j = 1:n,
    if j~=index_i,
        min_dist = 65535;
        min = 1;
        % 找到到其他点的最近的距离,存入min_dist,顶点号存入min
        for k = 1:n,
            % 找到最小的路径和距离,以确定这一次的最短路径
            if s(k)~=1 && ~isnan(dist(k)) && dist(k)<min_dist,
                min_dist = dist(k);
                min = k;
            end
        end
        % 如果没有连通的路径,则结束
        % 此种情况一般出现较少的,寻路断
        if isnan(min_dist),
            break;
        end
        
        s(min) = 1; % 确定一条最短路径,(index_i,min),min并入s
        for k = 1:n,
            if isnan(dist(k)),
                dist(k) = max_weight;
            end
            if s(k)~=1 && ~isnan(mat(min,k)) && dist(min) +…
                    mat(min,k) < dist(k),
                dist(k) = dist(min) + mat(min,k);
                path(k) = min;
            end
        end
    else
        %无操作
    end
end
% 输出路径 解读dist和path
fprintf(‘源点为%d最短路径如下:\n’,index_i);
for i = 1:n,
    each_path = [];
    if i ~= index_i,
        j = i;
        while(path(j)~=-1),
            each_path = [each_path j];
            j = path(j);
        end
        each_path = [each_path j];
        fprintf(‘第%d条:’,i);
        col_num = size(each_path,2);
        for k = 1:col_num,
            fprintf(‘顶点号%d ‘,each_path(col_num+1-k));
        end
        fprintf(‘,距离是%d\n’,dist(i));
    else
        fprintf(‘第%d条:顶点%d到它自己,距离为0\n’,i,index_i);
    end
end
end

—————————————————————————————————-

    另起一个m-file,取名dijkstra_algo_test.m,内容如下:

—————————————————————————————————–

% 邻接矩阵(如果不明白邻接矩阵的概念,网上搜一下就好了)
mat = [0 10 nan 30 99;nan 0 50 40 nan;nan nan 0 nan 10;nan nan 20 0 60;nan nan nan nan 0];
[dist,path] = dijkstra_algo(1,mat);

—————————————————————————————————–

    得到的结果包含两部分:一部分是接口的输出dist和path向量。另一部分是命令行的友好的文字显示:

《Dijkstra algorithm原理及使用》

    我们改变源点再来测试一下,把源点改成2号顶点。

《Dijkstra algorithm原理及使用》

    其中距离达到65536代表到这个顶点的距离是无穷大,也就是不连通。


    第二个接口是带顶点名字的,也就是又做了一些小改进,取名dijkstra_algo_name,新建一个m-file,取名dijkstra_algo_name.m,内容如下:

—————————————————————————————

% 任务目标:开发Dijkstra算法的接口,输入是源点号index_i、邻接矩阵mat,
% 和顶点名字name,输出是最短距离向量dist、最短路径阵向量path,
% 以及命令行输出详细规划(带name)
function [dist, path] = dijkstra_algo_name(index_i, mat,name)
max_weight = 65536;
n = size(mat,2);
% s用来记录已存的顶点,为1表示已存进s集合
s = zeros(1,n);
s(index_i) = 1;
% 初始化dist和path向量,向量运算复杂度较矩阵运算低
dist = zeros(1,n);
path = zeros(1,n);
for i = 1:n,
    dist(i) = mat(index_i,i);
    if ~isnan(dist(i)) && i~=index_i,
        path(i) = index_i;
    else
        path(i) = -1;
    end
end

for j = 1:n,
    if j~=index_i,
        min_dist = 65535;
        min = 1;
        % 找到到其他点的最近的距离,存入min_dist,顶点号存入min
        for k = 1:n,
            % 找到最小的路径和距离,以确定这一次的最短路径
            if s(k)~=1 && ~isnan(dist(k)) && dist(k)<min_dist,
                min_dist = dist(k);
                min = k;
            end
        end
        % 如果没有连通的路径,则结束
        % 此种情况一般出现较少的,寻路断
        if isnan(min_dist),
            break;
        end
        
        s(min) = 1; % 确定一条最短路径,(index_i,min),min并入s
        for k = 1:n,
            if isnan(dist(k)),
                dist(k) = max_weight;
            end
            if s(k)~=1 && ~isnan(mat(min,k)) && dist(min) +…
                    mat(min,k) < dist(k),
                dist(k) = dist(min) + mat(min,k);
                path(k) = min;
            end
        end
    else
        %无操作
    end
end
% 输出路径 解读dist和path
fprintf(‘源点为%s最短路径如下:\n’,name(index_i));
for i = 1:n,
    each_path = [];
    if i ~= index_i,
        j = i;
        while(path(j)~=-1),
            each_path = [each_path j];
            j = path(j);
        end
        each_path = [each_path j];
        fprintf(‘第%d条:’,i);
        col_num = size(each_path,2);
        for k = 1:col_num,
            fprintf(‘顶点%s ‘,name(each_path(col_num+1-k)));
        end
        fprintf(‘,距离是%d\n’,dist(i));
    else
        fprintf(‘第%d条:顶点%s到它自己,距离为0\n’,i,name(index_i));
    end
end
end

—————————————————————————————————

    同样,写测试代码,另建一个m-file,取名dijkstra_algo_name_test.m,内容如下:

—————————————————————————————————

% 邻接矩阵
mat = [0 10 nan 30 99;nan 0 50 40 nan;nan nan 0 nan 10;nan nan 20 0 60;nan nan nan nan 0];
% 邻接矩阵行列号对应顶点名称
name = [‘A’,’B’,’C’,’D’,’E’];
[dist,path] = dijkstra_algo_name(1,mat,name);

—————————————————————————————————-

    那么我们看一下运行结果:path和dist大家就自己去看吧,我们看一下解读过后的路径和距离

《Dijkstra algorithm原理及使用》

    我们和前面一样,把源点切换成2号顶点,也就是’B’,也就是把测试代码中把调用接口的参数改一下就可以,看一下结果

《Dijkstra algorithm原理及使用》

    测试通过。

    开发这两个接口的用意是,假如顶点数目很多,并且顶点名字不易导入,那么我们直接用第一个接口比较省力;如果需要更人性化的结果显示,并且顶点名字易于导入,那么我们就用第二个接口为好。

    如果大家看不太懂,可以直接用的。

    PS: 此博文允许任何人转载。



    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/Xyc19930930/article/details/77862422
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞