最近在公司参加了一个比赛,其中涉及的一个问题,可以简化成如是描述:一个二维矩阵,每个点都有权重,需要找出从指定起点到终点的最短路径。
马上就想到了Dijkstra算法,所以又重新温故了一遍,这里给出Java的实现。
而输出最短路径的时候,在网上也进行了查阅,没发现什么标准的方法,于是在下面的实现中,我给出了一种能够想到的比较精简的方式:利用prev[]数组进行递归输出。
[java]view plaincopy
1. package graph.dijsktra;
2.
3. import graph.model.Point;
4.
5. import java.util.*;
6.
7. /**
8. * Created by MHX on 2017/9/13.
9. */
10. publicclass Dijkstra {
11. privateint[][] map; // 地图结构保存
12. privateint[][] edges; // 邻接矩阵
13. privateint[] prev; // 前驱节点标号
14. privateboolean[] s; // S集合中存放到起点已经算出最短路径的点
15. privateint[] dist; // dist[i]表示起点到第i个节点的最短路径
16. privateint pointNum; // 点的个数
17. private Map<Integer, Point> indexPointMap; // 标号和点的对应关系
18. private Map<Point, Integer> pointIndexMap; // 点和标号的对应关系
19. privateint v0; // 起点标号
20. private Point startPoint; // 起点
21. private Point endPoint; // 终点
22. private Map<Point, Point> pointPointMap; // 保存点和权重的映射关系
23. private List<Point> allPoints; // 保存所有点
24. privateint maxX; // x坐标的最大值
25. privateint maxY; // y坐标的最大值
26.
27. public Dijkstra(int map[][], Point startPoint, Point endPoint) {
28. this.maxX = map.length;
29. this.maxY = map[0].length;
30. this.pointNum = maxX * maxY;
31. this.map = map;
32. this.startPoint = startPoint;
33. this.endPoint = endPoint;
34. init();
35. dijkstra();
36. }
37.
38. /**
39. * 打印指定起点到终点的最短路径
40. */
41. publicvoid printShortestPath() {
42. printDijkstra(pointIndexMap.get(endPoint));
43. }
44.
45. /**
46. * 初始化dijkstra
47. */
48. privatevoid init() {
49. // 初始化所有变量
50. edges = newint[pointNum][pointNum];
51. prev = newint[pointNum];
52. s = newboolean[pointNum];
53. dist = newint[pointNum];
54. indexPointMap = new HashMap<>();
55. pointIndexMap = new HashMap<>();
56. pointPointMap = new HashMap<>();
57. allPoints = new ArrayList<>();
58.
59. // 将map二维数组中的所有点转换成自己的结构
60. int count = 0;
61. for (int x = 0; x < maxX; ++x) {
62. for (int y = 0; y < maxY; ++y) {
63. indexPointMap.put(count, new Point(x, y));
64. pointIndexMap.put(new Point(x, y), count);
65. count++;
66. allPoints.add(new Point(x, y));
67. pointPointMap.put(new Point(x, y), new Point(x, y, map[x][y]));
68. }
69. }
70.
71. // 初始化邻接矩阵
72. for (int i = 0; i < pointNum; ++i) {
73. for (int j = 0; j < pointNum; ++j) {
74. if (i == j) {
75. edges[i][j] = 0;
76. } else {
77. edges[i][j] = 9999;
78. }
79. }
80. }
81.
82. // 根据map上的权重初始化edges,当然这种算法是没有单独加起点的权重的
83. for (Point point : allPoints) {
84. for (Point aroundPoint : getAroundPoints(point)) {
85. edges[pointIndexMap.get(point)][pointIndexMap.get(aroundPoint)] = aroundPoint.getValue();
86. }
87. }
88.
89. v0 = pointIndexMap.get(startPoint);
90.
91. for (int i = 0; i < pointNum; ++i) {
92. dist[i] = edges[v0][i];
93. if (dist[i] == 9999) {
94. // 如果从0点(起点)到i点最短路径是9999,即不可达
95. // 则i节点的前驱节点不存在
96. prev[i] = -1;
97. } else {
98. // 初始化i节点的前驱节点为起点,因为这个时候有最短路径的都是与起点直接相连的点
99. prev[i] = v0;
100. }
101. }
102.
103. dist[v0] = 0;
104. s[v0] = true;
105. }
106.
107. /**
108. * dijkstra核心算法
109. */
110. privatevoid dijkstra() {
111. for (int i = 1; i < pointNum; ++i) { // 此时有pointNum - 1个点在U集合中,需要循环pointNum - 1次
112. int minDist = 9999;
113. int u = v0;
114.
115. for (int j = 1; j < pointNum; ++j) { // 在U集合中,找到到起点最短距离的点
116. if (!s[j] && dist[j] < minDist) { // 不在S集合,就是在U集合
117. u = j;
118. minDist = dist[j];
119. }
120. }
121. s[u] = true; // 将这个点放入S集合
122.
123. for (int j = 1; j < pointNum; ++j) { // 以当前刚从U集合放入S集合的点u为基础,循环其可以到达的点
124. if (!s[j] && edges[u][j] < 9999) {
125. if (dist[u] + edges[u][j] < dist[j]) {
126. dist[j] = dist[u] + edges[u][j];
127. prev[j] = u;
128. }
129. }
130. }
131. }
132. }
133.
134. privatevoid printDijkstra(int endPointIndex) {
135. if (endPointIndex == v0) {
136. System.out.print(indexPointMap.get(v0) + ",");
137. return;
138. }
139. printDijkstra(prev[endPointIndex]);
140. System.out.print(indexPointMap.get(endPointIndex) + ",");
141. }
142.
143. private List<Point> getAroundPoints(Point point) {
144. List<Point> aroundPoints = new ArrayList<>();
145. int x = point.getX();
146. int y = point.getY();
147. aroundPoints.add(pointPointMap.get(new Point(x - 1, y)));
148. aroundPoints.add(pointPointMap.get(new Point(x, y + 1)));
149. aroundPoints.add(pointPointMap.get(new Point(x + 1, y)));
150. aroundPoints.add(pointPointMap.get(new Point(x, y - 1)));
151. aroundPoints.removeAll(Collections.singleton(null)); // 剔除不在地图范围内的null点
152. return aroundPoints;
153. }
154.
155. publicstaticvoid main(String[] args) {
156. int map[][] = {
157. {1, 2, 2, 2, 2, 2, 2},
158. {1, 0, 2, 2, 0, 2, 2},
159. {1, 2, 0, 2, 0, 2, 2},
160. {1, 2, 2, 0, 2, 0, 2},
161. {1, 2, 2, 2, 2, 2, 2},
162. {1, 1, 1, 1, 1, 1, 1}
163. }; // 每个点都代表权重,没有方向限制
164. Point startPoint = new Point(0, 3); // 起点
165. Point endPoint = new Point(5, 6); // 终点
166. Dijkstra dijkstra = new Dijkstra(map, startPoint, endPoint);
167. dijkstra.printShortestPath();
168. }
169. }
[java]view plaincopy
1. package graph.model;
2.
3. publicclass Point {
4. privateint x;
5. privateint y;
6. privateint value;
7.
8. public Point(int x, int y) {
9. this.x = x;
10. this.y = y;
11. }
12.
13. public Point(int x, int y, int value) {
14. this.x = x;
15. this.y = y;
16. this.value = value;
17. }
18.
19. publicint getX() {
20. return x;
21. }
22.
23. publicvoid setX(int x) {
24. this.x = x;
25. }
26.
27. publicint getY() {
28. return y;
29. }
30.
31. publicvoid setY(int y) {
32. this.y = y;
33. }
34.
35. publicint getValue() {
36. return value;
37. }
38.
39. publicvoid setValue(int value) {
40. this.value = value;
41. }
42.
43. @Override
44. public String toString() {
45. return"{" +
46. "x=" + x +
47. ", y=" + y +
48. '}';
49. }
50.
51. @Override
52. publicboolean equals(Object o) {
53. if (this == o) returntrue;
54. if (o == null || getClass() != o.getClass()) returnfalse;
55.
56. Point point = (Point) o;
57.
58. if (x != point.x) returnfalse;
59. return y == point.y;
60. }
61.
62. @Override
63. publicint hashCode() {
64. int result = x;
65. result = 31 * result + y;
66. return result;
67. }
68. } 返回搜狐,查看更多