问题描述

校园最短路径实验

1、给出校园中常用的几个点,如教室550、文宗楼、三个食堂、大操场、宿舍楼(自定)、校门口、体育场;
2、画图并给出其邻接矩阵(请合作完成);
3、用floyd算法求每对顶点间的最短路。
image.png


:::tips 迪杰斯特拉(Dijkstra)算法 :::

Dijkstra算法是经典的单源最短路径算法,用于计算源点到其它所有顶点的最短路径。在图 G=(V,E) 中,假设每条边 E[i] 的权值距离为 w[i],找到由源点 v0 到其余各点的最短路径。
适用:不含负权重的图

算法当中,对图的遍历方式为BFS(广度优先遍历)

代码

image.png

  1. /**
  2. * C++: Floyd算法获取最短路径(邻接矩阵)
  3. *
  4. */
  5. #include <iomanip>
  6. #include <iostream>
  7. #include <vector>
  8. #include <bits/stdc++.h>
  9. using namespace std;
  10. // 边的结构体
  11. class EData
  12. {
  13. public:
  14. char start; // 边的起点
  15. char end; // 边的终点
  16. int weight; // 边的权重
  17. public:
  18. EData(){}
  19. EData(char s, char e, int w):start(s),end(e),weight(w){}
  20. };
  21. class MatrixUDG {
  22. #define MAX 100
  23. #define INF (~(0x1<<31)) // 无穷大(即0X7FFFFFFF)
  24. private:
  25. char mVexs[MAX]; // 顶点集合
  26. int mVexNum; // 顶点数
  27. int mEdgNum; // 边数
  28. int mMatrix[MAX][MAX]; // 邻接矩阵
  29. public:
  30. // 创建图(自己输入数据)
  31. MatrixUDG();
  32. // 创建图(用已提供的矩阵)
  33. //MatrixUDG(char vexs[], int vlen, char edges[][2], int elen);
  34. MatrixUDG(char vexs[], int vlen, int matrix[][9]);
  35. ~MatrixUDG();
  36. // 深度优先搜索遍历图
  37. void DFS();
  38. // 广度优先搜索(类似于树的层次遍历)
  39. void BFS();
  40. // prim最小生成树(从start开始生成最小生成树)
  41. void prim(int start);
  42. // 克鲁斯卡尔(Kruskal)最小生成树
  43. void kruskal();
  44. // Dijkstra最短路径
  45. void dijkstra(int vs, int vexs[], int dist[]);
  46. // Floyd最短路径
  47. void floyd(int path[][MAX], int dist[][MAX]);
  48. // 打印矩阵队列图
  49. void print();
  50. private:
  51. // 读取一个输入字符
  52. char readChar();
  53. // 返回ch在mMatrix矩阵中的位置
  54. int getPosition(char ch);
  55. // 返回顶点v的第一个邻接顶点的索引,失败则返回-1
  56. int firstVertex(int v);
  57. // 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
  58. int nextVertex(int v, int w);
  59. // 深度优先搜索遍历图的递归实现
  60. void DFS(int i, int *visited);
  61. // 获取图中的边
  62. EData* getEdges();
  63. // 对边按照权值大小进行排序(由小到大)
  64. void sortEdges(EData* edges, int elen);
  65. // 获取i的终点
  66. int getEnd(int vends[], int i);
  67. };
  68. /*
  69. * 创建图(自己输入数据)
  70. */
  71. MatrixUDG::MatrixUDG()
  72. {
  73. char c1, c2;
  74. int i, j, weight, p1, p2;
  75. // 输入"顶点数"和"边数"
  76. cout << "input vertex number: ";
  77. cin >> mVexNum;
  78. cout << "input edge number: ";
  79. cin >> mEdgNum;
  80. if ( mVexNum < 1 || mEdgNum < 1 || (mEdgNum > (mVexNum * (mVexNum-1))))
  81. {
  82. cout << "input error: invalid parameters!" << endl;
  83. return ;
  84. }
  85. // 初始化"顶点"
  86. for (i = 0; i < mVexNum; i++)
  87. {
  88. cout << "vertex(" << i << "): ";
  89. mVexs[i] = readChar();
  90. }
  91. // 1. 初始化"边"的权值
  92. for (i = 0; i < mVexNum; i++)
  93. {
  94. for (j = 0; j < mVexNum; j++)
  95. {
  96. if (i==j)
  97. mMatrix[i][j] = 0;
  98. else
  99. mMatrix[i][j] = INF;
  100. }
  101. }
  102. // 2. 初始化"边"的权值: 根据用户的输入进行初始化
  103. for (i = 0; i < mEdgNum; i++)
  104. {
  105. // 读取边的起始顶点,结束顶点,权值
  106. cout << "edge(" << i << "): ";
  107. c1 = readChar();
  108. c2 = readChar();
  109. cin >> weight;
  110. p1 = getPosition(c1);
  111. p2 = getPosition(c2);
  112. if (p1==-1 || p2==-1)
  113. {
  114. cout << "input error: invalid edge!" << endl;
  115. return ;
  116. }
  117. mMatrix[p1][p2] = weight;
  118. mMatrix[p2][p1] = weight;
  119. }
  120. }
  121. /*
  122. * 创建图(用已提供的矩阵)
  123. *
  124. * 参数说明:
  125. * vexs -- 顶点数组
  126. * vlen -- 顶点数组的长度
  127. * matrix-- 矩阵(数据)
  128. */
  129. MatrixUDG::MatrixUDG(char vexs[], int vlen, int matrix[][9])
  130. {
  131. int i, j;
  132. // 初始化"顶点数"和"边数"
  133. mVexNum = vlen;
  134. // 初始化"顶点"
  135. for (i = 0; i < mVexNum; i++)
  136. mVexs[i] = vexs[i];
  137. // 初始化"边"
  138. for (i = 0; i < mVexNum; i++)
  139. for (j = 0; j < mVexNum; j++)
  140. mMatrix[i][j] = matrix[i][j];
  141. // 统计边的数目
  142. for (i = 0; i < mVexNum; i++)
  143. for (j = 0; j < mVexNum; j++)
  144. if (i!=j && mMatrix[i][j]!=INF)
  145. mEdgNum++;
  146. mEdgNum /= 2;
  147. }
  148. /*
  149. * 析构函数
  150. */
  151. MatrixUDG::~MatrixUDG()
  152. {
  153. }
  154. /*
  155. * 返回ch在mMatrix矩阵中的位置
  156. */
  157. int MatrixUDG::getPosition(char ch)
  158. {
  159. int i;
  160. for(i=0; i<mVexNum; i++)
  161. if(mVexs[i]==ch)
  162. return i;
  163. return -1;
  164. }
  165. /*
  166. * 读取一个输入字符
  167. */
  168. char MatrixUDG::readChar()
  169. {
  170. char ch;
  171. do {
  172. cin >> ch;
  173. } while(!((ch>='a'&&ch<='z') || (ch>='A'&&ch<='Z')));
  174. return ch;
  175. }
  176. /*
  177. * 返回顶点v的第一个邻接顶点的索引,失败则返回-1
  178. */
  179. int MatrixUDG::firstVertex(int v)
  180. {
  181. int i;
  182. if (v<0 || v>(mVexNum-1))
  183. return -1;
  184. for (i = 0; i < mVexNum; i++)
  185. if (mMatrix[v][i]!=0 && mMatrix[v][i]!=INF)
  186. return i;
  187. return -1;
  188. }
  189. /*
  190. * 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
  191. */
  192. int MatrixUDG::nextVertex(int v, int w)
  193. {
  194. int i;
  195. if (v<0 || v>(mVexNum-1) || w<0 || w>(mVexNum-1))
  196. return -1;
  197. for (i = w + 1; i < mVexNum; i++)
  198. if (mMatrix[v][i]!=0 && mMatrix[v][i]!=INF)
  199. return i;
  200. return -1;
  201. }
  202. /*
  203. * 深度优先搜索遍历图的递归实现
  204. */
  205. void MatrixUDG::DFS(int i, int *visited)
  206. {
  207. int w;
  208. visited[i] = 1;
  209. cout << mVexs[i] << " ";
  210. // 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
  211. for (w = firstVertex(i); w >= 0; w = nextVertex(i, w))
  212. {
  213. if (!visited[w])
  214. DFS(w, visited);
  215. }
  216. }
  217. /*
  218. * 深度优先搜索遍历图
  219. */
  220. void MatrixUDG::DFS()
  221. {
  222. int i;
  223. int visited[MAX]; // 顶点访问标记
  224. // 初始化所有顶点都没有被访问
  225. for (i = 0; i < mVexNum; i++)
  226. visited[i] = 0;
  227. cout << "DFS: ";
  228. for (i = 0; i < mVexNum; i++)
  229. {
  230. //printf("\n== LOOP(%d)\n", i);
  231. if (!visited[i])
  232. DFS(i, visited);
  233. }
  234. cout << endl;
  235. }
  236. /*
  237. * 广度优先搜索(类似于树的层次遍历)
  238. */
  239. void MatrixUDG::BFS()
  240. {
  241. int head = 0;
  242. int rear = 0;
  243. int queue[MAX]; // 辅组队列
  244. int visited[MAX]; // 顶点访问标记
  245. int i, j, k;
  246. for (i = 0; i < mVexNum; i++)
  247. visited[i] = 0;
  248. cout << "BFS: ";
  249. for (i = 0; i < mVexNum; i++)
  250. {
  251. if (!visited[i])
  252. {
  253. visited[i] = 1;
  254. cout << mVexs[i] << " ";
  255. queue[rear++] = i; // 入队列
  256. }
  257. while (head != rear)
  258. {
  259. j = queue[head++]; // 出队列
  260. for (k = firstVertex(j); k >= 0; k = nextVertex(j, k)) //k是为访问的邻接顶点
  261. {
  262. if (!visited[k])
  263. {
  264. visited[k] = 1;
  265. cout << mVexs[k] << " ";
  266. queue[rear++] = k;
  267. }
  268. }
  269. }
  270. }
  271. cout << endl;
  272. }
  273. /*
  274. * 打印矩阵队列图
  275. */
  276. void MatrixUDG::print()
  277. {
  278. int i,j;
  279. cout << "Martix Graph:" << endl;
  280. for (i = 0; i < mVexNum; i++)
  281. {
  282. for (j = 0; j < mVexNum; j++)
  283. cout << setw(10) << mMatrix[i][j] << " ";
  284. cout << endl;
  285. }
  286. }
  287. /*
  288. * prim最小生成树
  289. *
  290. * 参数说明:
  291. * start -- 从图中的第start个元素开始,生成最小树
  292. */
  293. void MatrixUDG::prim(int start)
  294. {
  295. int min,i,j,k,m,n,sum;
  296. int index=0; // prim最小树的索引,即prims数组的索引
  297. char prims[MAX]; // prim最小树的结果数组
  298. int weights[MAX]; // 顶点间边的权值
  299. // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
  300. prims[index++] = mVexs[start];
  301. // 初始化"顶点的权值数组",
  302. // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
  303. for (i = 0; i < mVexNum; i++ )
  304. weights[i] = mMatrix[start][i];
  305. // 将第start个顶点的权值初始化为0。
  306. // 可以理解为"第start个顶点到它自身的距离为0"。
  307. weights[start] = 0;
  308. for (i = 0; i < mVexNum; i++)
  309. {
  310. // 由于从start开始的,因此不需要再对第start个顶点进行处理。
  311. if(start == i)
  312. continue;
  313. j = 0;
  314. k = 0;
  315. min = INF;
  316. // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
  317. while (j < mVexNum)
  318. {
  319. // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
  320. if (weights[j] != 0 && weights[j] < min)
  321. {
  322. min = weights[j];
  323. k = j;
  324. }
  325. j++;
  326. }
  327. // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
  328. // 将第k个顶点加入到最小生成树的结果数组中
  329. prims[index++] = mVexs[k];
  330. // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
  331. weights[k] = 0;
  332. // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
  333. for (j = 0 ; j < mVexNum; j++)
  334. {
  335. // 当第j个节点没有被处理,并且需要更新时才被更新。
  336. if (weights[j] != 0 && mMatrix[k][j] < weights[j])
  337. weights[j] = mMatrix[k][j];
  338. }
  339. }
  340. // 计算最小生成树的权值
  341. sum = 0;
  342. for (i = 1; i < index; i++)
  343. {
  344. min = INF;
  345. // 获取prims[i]在mMatrix中的位置
  346. n = getPosition(prims[i]);
  347. // 在vexs[0...i]中,找出到j的权值最小的顶点。
  348. for (j = 0; j < i; j++)
  349. {
  350. m = getPosition(prims[j]);
  351. if (mMatrix[m][n]<min)
  352. min = mMatrix[m][n];
  353. }
  354. sum += min;
  355. }
  356. // 打印最小生成树
  357. cout << "PRIM(" << mVexs[start] << ")=" << sum << ": ";
  358. for (i = 0; i < index; i++)
  359. cout << prims[i] << " ";
  360. cout << endl;
  361. }
  362. /*
  363. * 获取图中的边
  364. */
  365. EData* MatrixUDG::getEdges()
  366. {
  367. int i,j;
  368. int index=0;
  369. EData *edges;
  370. edges = new EData[mEdgNum];
  371. for (i=0; i < mVexNum; i++)
  372. {
  373. for (j=i+1; j < mVexNum; j++)
  374. {
  375. if (mMatrix[i][j]!=INF)
  376. {
  377. edges[index].start = mVexs[i];
  378. edges[index].end = mVexs[j];
  379. edges[index].weight = mMatrix[i][j];
  380. index++;
  381. }
  382. }
  383. }
  384. return edges;
  385. }
  386. /*
  387. * 对边按照权值大小进行排序(由小到大)
  388. */
  389. void MatrixUDG::sortEdges(EData* edges, int elen)
  390. {
  391. int i,j;
  392. for (i=0; i<elen; i++)
  393. {
  394. for (j=i+1; j<elen; j++)
  395. {
  396. if (edges[i].weight > edges[j].weight)
  397. {
  398. // 交换"边i"和"边j"
  399. swap(edges[i], edges[j]);
  400. }
  401. }
  402. }
  403. }
  404. /*
  405. * 获取i的终点
  406. */
  407. int MatrixUDG::getEnd(int vends[], int i)
  408. {
  409. while (vends[i] != 0)
  410. i = vends[i];
  411. return i;
  412. }
  413. /*
  414. * 克鲁斯卡尔(Kruskal)最小生成树
  415. */
  416. void MatrixUDG::kruskal()
  417. {
  418. int i,m,n,p1,p2;
  419. int length;
  420. int index = 0; // rets数组的索引
  421. int vends[MAX]={0}; // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
  422. EData rets[MAX]; // 结果数组,保存kruskal最小生成树的边
  423. EData *edges; // 图对应的所有边
  424. // 获取"图中所有的边"
  425. edges = getEdges();
  426. // 将边按照"权"的大小进行排序(从小到大)
  427. sortEdges(edges, mEdgNum);
  428. for (i=0; i<mEdgNum; i++)
  429. {
  430. p1 = getPosition(edges[i].start); // 获取第i条边的"起点"的序号
  431. p2 = getPosition(edges[i].end); // 获取第i条边的"终点"的序号
  432. m = getEnd(vends, p1); // 获取p1在"已有的最小生成树"中的终点
  433. n = getEnd(vends, p2); // 获取p2在"已有的最小生成树"中的终点
  434. // 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路
  435. if (m != n)
  436. {
  437. vends[m] = n; // 设置m在"已有的最小生成树"中的终点为n
  438. rets[index++] = edges[i]; // 保存结果
  439. }
  440. }
  441. delete[] edges;
  442. // 统计并打印"kruskal最小生成树"的信息
  443. length = 0;
  444. for (i = 0; i < index; i++)
  445. length += rets[i].weight;
  446. cout << "Kruskal=" << length << ": ";
  447. for (i = 0; i < index; i++)
  448. cout << "(" << rets[i].start << "," << rets[i].end << ") ";
  449. cout << endl;
  450. }
  451. /*
  452. * Dijkstra最短路径。
  453. * 即,统计图中"顶点vs"到其它各个顶点的最短路径。
  454. *
  455. * 参数说明:
  456. * vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
  457. * prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
  458. * dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
  459. */
  460. void MatrixUDG::dijkstra(int vs, int prev[], int dist[])
  461. {
  462. int i,j,k;
  463. int min;
  464. int tmp;
  465. int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
  466. // 初始化
  467. for (i = 0; i < mVexNum; i++)
  468. {
  469. flag[i] = 0; // 顶点i的最短路径还没获取到。
  470. prev[i] = 0; // 顶点i的前驱顶点为0。
  471. dist[i] = mMatrix[vs][i]; // 顶点i的最短路径为"顶点vs"到"顶点i"的权。
  472. }
  473. // 对"顶点vs"自身进行初始化
  474. flag[vs] = 1;
  475. dist[vs] = 0;
  476. // 遍历mVexNum-1次;每次找出一个顶点的最短路径。
  477. for (i = 1; i < mVexNum; i++)
  478. {
  479. // 寻找当前最小的路径;
  480. // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
  481. min = INF;
  482. for (j = 0; j < mVexNum; j++)
  483. {
  484. if (flag[j]==0 && dist[j]<min)
  485. {
  486. min = dist[j];
  487. k = j;
  488. }
  489. }
  490. // 标记"顶点k"为已经获取到最短路径
  491. flag[k] = 1;
  492. // 修正当前最短路径和前驱顶点
  493. // 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
  494. for (j = 0; j < mVexNum; j++)
  495. {
  496. tmp = (mMatrix[k][j]==INF ? INF : (min + mMatrix[k][j]));
  497. if (flag[j] == 0 && (tmp < dist[j]) )
  498. {
  499. dist[j] = tmp;
  500. prev[j] = k;
  501. }
  502. }
  503. }
  504. // 打印dijkstra最短路径的结果
  505. cout << "dijkstra(" << mVexs[vs] << "): " << endl;
  506. for (i = 0; i < mVexNum; i++)
  507. cout << " shortest(" << mVexs[vs] << ", " << mVexs[i] << ")=" << dist[i] << endl;
  508. }
  509. /*
  510. * floyd最短路径。
  511. * 即,统计图中各个顶点间的最短路径。
  512. *
  513. * 参数说明:
  514. * path -- 路径。path[i][j]=k表示,"顶点i"到"顶点j"的最短路径会经过顶点k。
  515. * dist -- 长度数组。即,dist[i][j]=sum表示,"顶点i"到"顶点j"的最短路径的长度是sum。
  516. */
  517. void MatrixUDG::floyd(int path[][MAX], int dist[][MAX])
  518. {
  519. int i,j,k;
  520. int tmp;
  521. // 初始化
  522. for (i = 0; i < mVexNum; i++)
  523. {
  524. for (j = 0; j < mVexNum; j++)
  525. {
  526. dist[i][j] = mMatrix[i][j]; // "顶点i"到"顶点j"的路径长度为"i到j的权值"。
  527. path[i][j] = j; // "顶点i"到"顶点j"的最短路径是经过顶点j。
  528. }
  529. }
  530. // 计算最短路径
  531. for (k = 0; k < mVexNum; k++)
  532. {
  533. for (i = 0; i < mVexNum; i++)
  534. {
  535. for (j = 0; j < mVexNum; j++)
  536. {
  537. // 如果经过下标为k顶点路径比原两点间路径更短,则更新dist[i][j]和path[i][j]
  538. tmp = (dist[i][k]==INF || dist[k][j]==INF) ? INF : (dist[i][k] + dist[k][j]);
  539. if (dist[i][j] > tmp)
  540. {
  541. // "i到j最短路径"对应的值设,为更小的一个(即经过k)
  542. dist[i][j] = tmp;
  543. // "i到j最短路径"对应的路径,经过k
  544. path[i][j] = path[i][k];
  545. }
  546. }
  547. }
  548. }
  549. char dot[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'};
  550. // 打印floyd最短路径的结果
  551. cout << "floyd各个地点的最短路径矩阵如下: " << endl;
  552. cout << " ";
  553. for (int k = 0;k<9;k++){
  554. cout << dot[k] << " ";
  555. }
  556. cout << "\n";
  557. for (i = 0; i < mVexNum; i++){
  558. cout << dot[i] << ": ";
  559. for (j = 0; j < mVexNum; j++)
  560. cout << setw(2) << dist[i][j] << " ";
  561. cout << endl;
  562. }
  563. }
  564. int main()
  565. {
  566. int prev[MAX] = {0};
  567. int dist[MAX] = {0};
  568. int path[MAX][MAX] = {0}; // 用于保存floyd路径
  569. int floy[MAX][MAX] = {0}; // 用于保存floyd长度
  570. char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'};
  571. int matrix[][9] = {
  572. /*A*//*B*//*C*//*D*//*E*//*F*//*G*//*H*//*I*/
  573. /*A西门*/ { 0, 615, 435, 210, 790, INF, INF, INF, INF},
  574. /*B550教室*/ { 615, 0, 144, 620, 380, 822, INF, INF, INF},
  575. /*C文宗楼*/ { 435, 144, 0, INF, 265, INF, INF, INF, INF},
  576. /*D二餐*/ { 210, 620, INF, 0, 480, INF, INF, INF, 170},
  577. /*E图书馆*/ { 790, 380, 265, 480, 0, 620, 735, 310, 700},
  578. /*F北门*/ { INF, 822, INF, INF, 620, 0, 500, INF, INF},
  579. /*G体育馆*/ { INF, INF, INF, INF, 735, 500, 0, 556, INF},
  580. /*H一餐*/ { INF, INF, INF, INF, 310, INF, 556, 0, 420},
  581. /*I16号楼*/ { INF, INF, INF, 170, 700, INF, INF, 420, 0}};
  582. int vlen = sizeof(vexs)/sizeof(vexs[0]);
  583. MatrixUDG* pG;
  584. // 自定义"图"(输入矩阵队列)
  585. //pG = new MatrixUDG();
  586. // 采用已有的"图"
  587. pG = new MatrixUDG(vexs, vlen, matrix);
  588. //pG->print(); // 打印图
  589. //pG->DFS(); // 深度优先遍历
  590. //pG->BFS(); // 广度优先遍历
  591. //pG->prim(0); // prim算法生成最小生成树
  592. //pG->kruskal(); // Kruskal算法生成最小生成树
  593. // dijkstra算法获取"第4个顶点"到其它各个顶点的最短距离
  594. //pG->dijkstra(3, prev, dist);
  595. // floyd算法获取各个顶点之间的最短距离
  596. pG->floyd(path, floy);
  597. return 0;
  598. }

参考