2、Dijkstra

2.1、算法简介

迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又称为狄克斯特拉算法;

它是从一个节点遍历其余各个节点的最短路径算法,解决的是有权图中最短路径问题。

主要特点:从起点开始,采用贪心算法的策略,每次遍历到初始点距离最近且未访问过的顶点的邻接点,直到扩展到终点为止。

2.2、算法思路

如上图G=(V,E)是一个带权有向图,把图中节点集合V分成两组:

第一组为已经求出最短路径的节点集合(用S表示,初始时S只有一个源点,即:D(0),之后每求得一条最短路径,就将该节点加入到集合S中,指导全部节点都加入到S中,算法就结束了);第二组为其余未确定最短路径的节点集合(U表示),按最短路径长度的递增次序把第二组的节点加入S中。在加入的过程中,总保持从源点V到S中节点的最短路径长度不大于从源点V到U中任何节点的最短路径长度。

此外,每个节点对应一个距离,S中的节点的距离就是从V到此节点的最短路径长度,U中的节点的距离,是从V到此节点只包含S中的节点为中间节点的当前最短路径长度。

具体流程:

设D为起点,A为终点,找到D~A的最短路径

1、S中只包含起节点D(0);U包含除S外的其它节点,如C节点,C与D相邻,所以C(3)表示C到D的距离为3,而F与D不相邻,所以设F到D的距离为∞。

2、在U中选出最短节点,这里为C(3),将C移到S中,并在U中删除。

3、按上述方法依次选出,在U中选出最短节点,此时为E(4),将E移到S中,并在U中删除。

4、此时就要对比DCB=13;DCF=9;DEF=6;DEG=12的距离,所以将F(6)移到S中,并在U中删除;

5、依次按照上述方法推算

6、直到U为空集,此时,可以得到最短距离:A(22) = DEF~A =4+2+16;

2.3、算法具体实现

defColorMap.m文件:

作用:生成栅格图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function [field,cmap] = defColorMap(rows, cols)
cmap = [1 1 1; ... % 1-白色-空地
0 0 0; ... % 2-黑色-静态障碍
1 0 0; ... % 3-红色-动态障碍
1 1 0;... % 4-黄色-起始点
1 0 1;... % 5-品红-目标点
0 1 0; ... % 6-绿色-到目标点的规划路径
0 1 1]; % 7-青色-动态规划的路径

% 构建颜色MAP图
colormap(cmap);

% 定义栅格地图全域,并初始化空白区域
field = ones(rows, cols);

% 障碍物区域
obsRate = 0.3;
obsNum = floor(rows*cols*obsRate);
obsIndex = randi([1,rows*cols],obsNum,1);
field(obsIndex) = 2;

getNeighborNodes.m文件:

作用:搭建个节点之间的关系;实现查找当前父节点临近的周围8个子节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
function neighborNodes = getNeighborNodes(rows, cols, lineIndex, field)
[row, col] = ind2sub([rows,cols], lineIndex);
neighborNodes = inf(8,2);

%% 查找当前父节点临近的周围8个子节点
% 左上节点
if row-1 > 0 && col-1 > 0
child_node_sub = [row-1, col-1];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(1,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(1,2) = cost;
end
end

% 上节点
if row-1 > 0
child_node_sub = [row-1, col];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(2,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(2,2) = cost;
end
end

% 右上节点
if row-1 > 0 && col+1 <= cols
child_node_sub = [row-1, col+1];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(3,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(3,2) = cost;
end
end

% 左节点
if col-1 > 0
child_node_sub = [row, col-1];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(4,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(4,2) = cost;
end
end

% 右节点
if col+1 <= cols
child_node_sub = [row, col+1];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(5,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(5,2) = cost;
end
end

% 左下节点
if row+1 <= rows && col-1 > 0
child_node_sub = [row+1, col-1];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(6,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(6,2) = cost;
end
end

% 7.下节点
if row+1 <= rows
child_node_sub = [row+1, col];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(7,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(7,2) = cost;
end
end

% 8.右下节点
if row+1 <= rows && col+1 <= cols
child_node_sub = [row+1, col+1];
child_node_line = sub2ind([rows,cols], child_node_sub(1), child_node_sub(2));
neighborNodes(8,1) = child_node_line;
if field(child_node_sub(1), child_node_sub(2)) ~= 2
cost = norm(child_node_sub - [row, col]);
neighborNodes(8,2) = cost;
end
end

Dijkstra.m文件:

具体的算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
% 基于栅格地图的机器人路径规划算法
% 第2节:Dijkstra算法
clc
clear
close all

%% 栅格界面、场景定义
% 行数和列数
rows = 10;
cols = 20;
[field,cmap] = defColorMap(rows, cols);

% 起点、终点、障碍物区域
startPos = 2;
goalPos = rows*cols-2;
field(startPos) = 4;
field(goalPos) = 5;

%% 算法初始化
% S/U的第一列表示栅格节点线性索引编号
% 对于S,第二列表示从源节点到本节点已求得的最小距离,不再变更;
% 对于U,第二列表示从源节点到本节点暂时求得的最小距离,可能会变更
U(:,1) = (1: rows*cols)';
U(:,2) = inf;
S = [startPos, 0];
U(startPos,:) = [];

% 更新起点的邻节点及代价
neighborNodes = getNeighborNodes(rows, cols, startPos, field);
for i = 1:8
childNode = neighborNodes(i,1);

% 判断该子节点是否存在
if ~isinf(childNode)
idx = find(U(:,1) == childNode);
U(idx,2) = neighborNodes(i,2);
end
end



% S集合的最优路径集合
for i = 1:rows*cols
path{i,1} = i;
end
for i = 1:8
childNode = neighborNodes(i,1);
if ~isinf(neighborNodes(i,2))
path{childNode,2} = [startPos,neighborNodes(i,1)];
end
end


%% 循环遍历
while ~isempty(U)

% 在U集合找出当前最小距离值的节点,视为父节点,并移除该节点至S集合中
[dist_min, idx] = min(U(:,2));
parentNode = U(idx, 1);
S(end+1,:) = [parentNode, dist_min];
U(idx,:) = [];

% 获得当前节点的临近子节点
neighborNodes = getNeighborNodes(rows, cols, parentNode, field);

% 依次遍历邻近子节点,判断是否在U集合中更新邻节点的距离值
for i = 1:8

% 需要判断的子节点
childNode = neighborNodes(i,1);
cost = neighborNodes(i,2);
if ~isinf(childNode) && ~ismember(childNode, S)

% 找出U集合中节点childNode的索引值
idx_U = find(childNode == U(:,1));

% 判断是否更新
if dist_min + cost < U(idx_U, 2)
U(idx_U, 2) = dist_min + cost;

% 更新最优路径
path{childNode, 2} = [path{parentNode, 2}, childNode];
end
end
end
end


%% 画栅格界面
% 找出目标最优路径
path_opt = path{goalPos,2};
field(path_opt(2:end-1)) = 6;

% 画栅格图
image(1.5,1.5,field);
grid on;
set(gca,'gridline','-','gridcolor','k','linewidth',2,'GridAlpha',0.5);
set(gca,'xtick',1:cols+1,'ytick',1:rows+1);
axis image;
2.3.1、程序详解

①、 算法初始化

1
2
3
4
5
6
7
8
%% 算法初始化
% S/U的第一列表示栅格节点线性索引编号
% 对于S,第二列表示从源节点到本节点已求得的最小距离,不再变更;
% 对于U,第二列表示从源节点到本节点暂时求得的最小距离,可能会变更
U(:,1) = (1: rows*cols)';
U(:,2) = inf;
S = [startPos, 0];
U(startPos,:) = [];
1
2
3
对U集合进程初始化,生成如下表,第一列未线性索引值,第二列为距离都设为∞
U(:,1) = (1: rows*cols)';
U(:,2) = inf;
1
2
3
%将初始节点放入S集合中,并设置它的距离为0;再U集合中删除初始节点!
S = [startPos, 0];
U(startPos,:) = [];

②、更新起点的邻节点及代价

1
2
3
4
5
6
7
8
9
10
11
% 更新起点的邻节点及代价
neighborNodes = getNeighborNodes(rows, cols, startPos, field);
for i = 1:8
childNode = neighborNodes(i,1);

% 判断该子节点是否存在
if ~isinf(childNode)
idx = find(U(:,1) == childNode);
U(idx,2) = neighborNodes(i,2);
end
end

这里调用了getNeighborNodes函数实现查找当前父节点临近的周围8个子节点

有三种情况:

①、两列都为inf,即这个节点不存在;

②、两个都是数字,即此节点存在,且为自由空间(可以走的节点);

③、第一列是数字,第二列是inf,即此节点为障碍物

for i = 1:8;之后进行8次循环,判断该子节点是否存在,如果子节点存在,则将从父节点到子节点的距离存放到U集合中;

如上图中D是父节点,它有两个子节点C和E,那么就要再U集合中更新D到这两个节点的距离为3和4。

③、S集合的最优路径集合

1
2
3
4
5
6
7
8
9
10
% S集合的最优路径集合
for i = 1:rows*cols
path{i,1} = i;
end
for i = 1:8
childNode = neighborNodes(i,1);
if ~isinf(neighborNodes(i,2))
path{childNode,2} = [startPos,neighborNodes(i,1)];
end
end

这里是生成初始节点的最优路径

④、进行循环,循环的条件是U集合不为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
while ~isempty(U)

% 在U集合找出当前最小距离值的节点,视为父节点,并移除该节点至S集合中
[dist_min, idx] = min(U(:,2));
parentNode = U(idx, 1);
S(end+1,:) = [parentNode, dist_min];
U(idx,:) = [];

% 获得当前节点的临近子节点
neighborNodes = getNeighborNodes(rows, cols, parentNode, field);

% 依次遍历邻近子节点,判断是否在U集合中更新邻节点的距离值
for i = 1:8

% 需要判断的子节点
childNode = neighborNodes(i,1);
cost = neighborNodes(i,2);
if ~isinf(childNode) && ~ismember(childNode, S)

% 找出U集合中节点childNode的索引值
idx_U = find(childNode == U(:,1));

% 判断是否更新
if dist_min + cost < U(idx_U, 2)
U(idx_U, 2) = dist_min + cost;

% 更新最优路径
path{childNode, 2} = [path{parentNode, 2}, childNode];
end
end
end
end

得出个节点的距离值,进行排序后得到最优路径!

Dijkstra算法动态效果实现图:

2