1. 简介
Dijkstra 算法,是由荷兰计算机科学家 Edsger Wybe Dijkstra 在1956年发现的算法,戴克斯特拉算法使用类似广度优先搜索的方法解决赋权图的单源最短路径问题。Dijkstra 算法原始版本仅适用于找到两个顶点之间的最短路径,后来更常见的变体固定了一个顶点作为源结点然后找到该顶点到图中所有其它结点的最短路径,产生一个最短路径树。本算法每次取出未访问结点中距离最小的,用该结点更新其他结点的距离。需要注意的是绝大多数的Dijkstra 算法不能有效处理带有负权边的图。
设一个赋权有向图。其中的每条边的权值为一个非负的实数,该权值表示从顶点到顶点的距离。并设一单源点。现在我们的任务是:找出从源点s出发,到中所有的节点的最短路径。
我们来看一个具体的例子:
这是一个具有 个顶点的赋权有向图,其顶点集合为,其权值分别为
现在我们选定为原点s:
则从源点出发,到中所有顶点的最短路径分别为:
即:
其中, 表示从源点出发,到中的顶点的最短路径。
注: 最短路径可以理解为所有可能的路径中总权和最小的那一条路径。举一个再简单不过的例子:你开车从城市A到城市B,假设有很多条路可以走,最短的那条路就是最短路径,总权和可以理解为总的公里数。
以上是我们通过观察和计算比对出来的最短路径,下面我们就来看看Dijkstra 算法是如何帮助我们找到这些所有的最短路径的。
在开始之前,有几个概念需要明确一下。
- 定义一个集合S,如果集合 中的某个顶点 在集合 中了,那么就说明从源点 到顶点 的最短路径已经被找到,而在算法一开始的时候,集合 中只有源点 。即:
而且,当且仅当S=V的时候算法执行完毕。此时顶点集V中的所有元素都被放进了集合S中,也就是说除了源点以外的所有从源点出发到其余所有顶点的最短路径已被找到。
注:当然了,你也可以认为源点到自己本身的最短路径也被找到了。对于任意一个 无自环的源点,它到自己本身的最短路径都是0。
- 下面这个概念可能稍微有些抽象,不过没有关系,这里理解不了的话我们一会讲例子的时候会进行具体说明。这个概念叫做从源点s到顶点(一开始 )的相对于集合 的最短路径。 即从源点s到顶点的路径中间只能经过已经包含在集合S中的顶点,而不能经过其余的还未在集合S中的顶点。而这个相对于集合S的最短路径的长度我们记作:
而我们之前的 表示的是全局的从源点 到顶点 的最短路径,这个最短路径没有限制“必须在路径中间只能经过已经包含在集合 中的顶点”,这个全局的最短路径才是我们要的最终解。所以,一般有关系:
而我们的Dijkstra 算法要做的就是通过不断计算进而不断的扩充集合S,当集合S不断被扩充的时候,相对于集合S的最短路径会越来越短,直到入集合S之时,此时我们便得到了,且此时有。下面我们来看看算法的设计思想:
然后是Dijkstra 算法的伪码:
下面我们来解释一下这个伪码:
下面我们开始讲例子,我们还是以图片1中的赋权有向图进行说明。
下面我用绿色表示被放入集合S中的顶点:
其余发生变化的顶点分析类似,大家可以自己试试。
现在算法从头到尾被执行了一遍了,然后我们回到第五步判断 while 循环的条件还是否为真,此时:
2. 代码实现
function [route,numExpanded] = DijkstraGrid (input_map, start_coords, dest_coords)
% Run Dijkstra's algorithm on a grid.
% Inputs :
% input_map : a logical array where the freespace cells are false or 0 and
% the obstacles are true or 1
% start_coords and dest_coords : Coordinates of the start and end cell
% respectively, the first entry is the row and the second the column.
% Output :
% route : An array containing the linear indices of the cells along the
% shortest route from start to dest or an empty array if there is no
% route. This is a single dimensional vector
% numExpanded: Remember to also return the total number of nodes
% expanded during your search. Do not count the goal node as an expanded node.
% set up color map for display
% 1 - white - clear cell
% 2 - black - obstacle
% 3 - red = visited
% 4 - blue - on list
% 5 - green - start
% 6 - yellow - destination
cmap = [1 1 1; ...
0 0 0; ...
1 0 0; ...
0 0 1; ...
0 1 0; ...
1 1 0; ...
0.5 0.5 0.5];
colormap(cmap);
% variable to control if the map is being visualized on every
% iteration
drawMapEveryTime = true;
[nrows, ncols] = size(input_map);
% map - a table that keeps track of the state of each grid cell
map = zeros(nrows,ncols);
map(~input_map) = 1; % Mark free cells
map(input_map) = 2; % Mark obstacle cells
% Generate linear indices of start and dest nodes
start_node = sub2ind(size(map), start_coords(1), start_coords(2));
dest_node = sub2ind(size(map), dest_coords(1), dest_coords(2));
map(start_node) = 5;
map(dest_node) = 6;
% Initialize distance array
distanceFromStart = Inf(nrows,ncols);
% For each grid cell this array holds the index of its parent
parent = zeros(nrows,ncols);
distanceFromStart(start_node) = 0;
% keep track of number of nodes expanded
numExpanded = 0;
% Main Loop
while true
% Draw current map
map(start_node) = 5;
map(dest_node) = 6;
% make drawMapEveryTime = true if you want to see how the
% nodes are expanded on the grid.
if (drawMapEveryTime)
image(1.5, 1.5, map);
grid on;
axis image;
drawnow;
end
% Find the node with the minimum distance
[min_dist, current] = min(distanceFromStart(:));
if ((current == dest_node) || isinf(min_dist))
break;
end;
% Update map
map(current) = 3; % mark current node as visited
numExpanded=numExpanded+1;
% Compute row, column coordinates of current node
[i, j] = ind2sub(size(distanceFromStart), current);
% *********************************************************************
% YOUR CODE BETWEEN THESE LINES OF STARS
% Visit each neighbor of the current node and update the map, distances
% and parent tables appropriately.
action=[-1 0; 1 0; 0 -1; 0 1];%上,下,左,右
for a=1:4
expand=[i,j]+action(a,:);
expand1=expand(1,1);
expand2=expand(1,2);
%不超出边界,不穿越障碍,不在CLOSED列表里,则进行扩展
if ( expand1>=1 && expand1<=nrows && expand2>=1 && expand2<=ncols && map(expand1,expand2)~=2 && map(expand1,expand2)~=3 && map(expand1,expand2)~=5 )
% if ( expand1>=1 && expand1<=nrows && expand2>=1 && expand2<=ncols && map(expand1,expand2)~=2 && map(expand1,expand2)~=3 && map(expand1,expand2)~=5)
if ( distanceFromStart(expand1,expand2)> distanceFromStart(i,j)+1 )
distanceFromStart(expand1,expand2)= distanceFromStart(i,j)+1;
parent(expand1,expand2)=current;
map(expand1,expand2)=4;
end
end
end
distanceFromStart(current) = Inf; % remove this node from further consideration
%*********************************************************************
end
%% Construct route from start to dest by following the parent links
if (isinf(distanceFromStart(dest_node)))
route = [];
else
route = [dest_node];
while (parent(route(1)) ~= 0)
route = [parent(route(1)), route];
end
% Snippet of code used to visualize the map and the path
for k = 2:length(route) - 1
map(route(k)) = 7;
pause(0.1);
image(1.5, 1.5, map);
grid on;
axis image;
end
end
end