最短路徑--dijkstra演算法、弗洛伊德(Floyd)演算法(帶路徑輸出)
####最短路徑
求最短路徑重要性不言而喻,下面直接分析兩個演算法。
分類:
1:從某個源點到其餘個點的最短路徑
迪傑斯特拉(Dijkstra)演算法
2:每一對之間的最短路徑
弗洛伊德(Floyd)演算法
####一:儲存結構
鄰接矩陣(這裡不再重複講了,請參考):
https://blog.csdn.net/weixin_39956356/article/details/80470091
相關程式碼:
//鄰接矩陣
#define INT__MAX 65000 //最大值65535,表示兩頂點沒有聯絡
#define MAX_VERTEX_NUM 20 //最多頂點數
typedef char VertexType;
typedef int EdgeType;
//頂點資訊
typedef struct ArcCell {
EdgeType wight;
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //二維陣列
//弧的資訊
typedef struct MGraph {
VertexType vexs[MAX_VERTEX_NUM]; //頂點向量
AdjMatrix arcs; //鄰接矩陣
int vexnum, arcnum; //圖的當前頂點數和弧數
}MGraph;
####建立有向圖
注意一下(有向圖):G.arcs[i][j].wight = w;
相關程式碼:
//建立無向圖的鄰接矩陣
void CreatMGraph(MGraph &G)
{
for (int i = 0; i < G.vexnum; i++) {
printf("Please enter %d data:", i + 1);
scanf(" %c", &G.vexs[i]); //輸入頂點值
}
for (int i = 0; i<G.vexnum; i++)
for (int j = 0; j<G.vexnum; j++)
G.arcs[i][j].wight = INT__MAX; //鄰接矩陣初始化
VertexType v1, v2;
EdgeType w;
int i, j;
printf("Please input the two data of arc and wight,for example: A C 5\n");
for (int k = 0; k < G.arcnum; k++) {
printf("The %d arc: ", k + 1);
scanf(" %c", &v1); //輸入第一個頂點
getchar(); //把輸入的空格讀走
v2 = getchar(); //輸入弧的第二個頂點
scanf("%d", &w); //輸入權值
i = LocateVex(G, v1); //獲取弧的第一個節點位置
j = LocateVex(G, v2); //獲取弧的第二個節點位置
G.arcs[i][j].wight = w; //把權值存放在鄰接矩陣中
}
}
####二:迪傑斯特拉(Dijkstra)演算法,帶輸出路徑
自己將這個演算法分成兩部分:
一:最短路徑長度
二:最短路徑
####首先,我們先看最短路徑長度問題
1:這裡需要引入一個輔助陣列Dij[],每次遍歷一遍陣列得到本次陣列中最小值及所在位置。
2:找到最小值,併入S集
3:更新新的路徑值
判斷條件:還沒有併入S集,且有更小的路徑值
if (!isGetShortestPath[w] && (min + G.arcs[minIndex][w].wight < Dij[w]))
####其次,最短路徑
我拿一個圖來講解,整個流程。不防舉A->E。它的最短路徑是A->C->E,我們分析下
1:這裡需要引入一個輔助Code陣列code[][],記錄走過的最短路徑,初始值不防賦值為’!’。
2:清空E結點該行
3:把C結點路徑拷貝到E中
4:在第一個 ! ,新增尾結點E
圖6及輸出:
補充:這裡再多說一句,動態二維陣列分配
######先分配行,後分配列
typedef char **shortestCode; //最短路徑編碼
/******************************************************************
** 動態分配二維陣列
*******************************************************************/
code = (shortestCode)malloc(G.vexnum * sizeof(char *)); //分配G.vexnum行
for (int i = 0; i < G.vexnum; ++i) { //為每行分配G.vexnum列
code[i] = (char *)malloc(G.vexnum * sizeof(char));
}
相關程式碼
/***********************************************************************************************
** 迪傑斯特拉(Dijkstra)演算法
** v0 :任給一個起始點
** &code:最短路徑線路(這裡的&是必須的,如果沒有,相當於傳進來沒有初始化的實體,編譯器報錯)
************************************************************************************************/
void ShortestPath_DIJ(MGraph &G, char v0, shortestCode &code)
{
int v0Index = LocateVex(G, v0);
bool *isGetShortestPath = (bool *)malloc(G.vexnum * sizeof(bool)); //是否併入S集
int *Dij = (int *)malloc(G.vexnum * sizeof(int)); //最短路徑和
/******************************************************************
** 動態分配二維陣列
*******************************************************************/
code = (shortestCode)malloc(G.vexnum * sizeof(char *)); //分配G.vexnum行
for (int i = 0; i < G.vexnum; ++i) { //為每行分配G.vexnum列
code[i] = (char *)malloc(G.vexnum * sizeof(char));
}
for (int i = 0; i < G.vexnum; i++) { //初始所有點都不併入S集,把頂點的權值賦給Dij[]
isGetShortestPath[i] = false;
Dij[i] = G.arcs[v0Index][i].wight;
for (int w = 0; w < G.vexnum; w++)
code[i][w] = '!'; //設code[][]初值為!,即沒有路徑
if (Dij[i] < INT__MAX)
{
code[i][0] = v0; //到i最短路徑經過的第一個頂點是v0
code[i][1] = G.vexs[i]; //到i最短路徑經過的第二個頂點是i
}
}
isGetShortestPath[v0Index] = true; //把頂點併入S集
int min, minIndex;
for (int i = 1; i < G.vexnum; i++) {
min = INT__MAX; //不妨把min放置為最大值
/******************************************************************
** 獲取Dij陣列中最小值-----這是其他還沒有併入S集 路徑上的一個子集
** 對應的陣列位置----------為方便下面從這結點出發的弧遍歷找最小值
*******************************************************************/
for (int w = 0; w < G.vexnum; w++) {
if (!isGetShortestPath[w])
if (Dij[w] < min)
{
minIndex = w;
min = Dij[w];
}
}
isGetShortestPath[minIndex] = true; //把位置為minIndex的結點併入S集
for (int w = 0; w < G.vexnum; w++) { //如果有更小的路徑,替換原來的路徑和最小值
if (!isGetShortestPath[w] && (min + G.arcs[minIndex][w].wight < Dij[w]))
{
Dij[w] = min + G.arcs[minIndex][w].wight; //更新Dij[]值
for (int j = 0; j < G.vexnum; j++) { //如果Dij[]值改變,新節點路徑清空
code[w][j] = '!';
}
for (int j = 0; j < G.vexnum; j++) {
code[w][j] = code[minIndex][j]; //把minIndex結點路徑全部拷貝到w中去
if (code[w][j] == '!')
{
code[w][j] = G.vexs[w]; //在遇到第一個'!',將新節點放在後面
break;
}
}
}
}
}
printf("\n迪傑斯特拉(Dijkstra)最短路徑長度與最短路徑如下所示:\n");
for (int i = 1; i < G.vexnum; i++) {
if (Dij[i] != INT__MAX)
{
printf("從頂點 %c ---> %c 最短路徑長度為:%d\t最短路徑為:", v0, G.vexs[i], Dij[i]);
for(int j = 0; j < G.vexnum; j++)
{
if(code[i][j] != '!')
printf(" %c",code[i][j]); //輸出最短路徑
}
printf("\n");
}
else if (Dij[i] == INT__MAX)
{
printf("從頂點 %c ---> %c 不可到達!!!\n", v0, G.vexs[i]); //不存在最短路徑
}
}
free(Dij); //釋放Dij[][]空間
free(isGetShortestPath); //釋放isGetShortestPath[]空間
}
####程式輸出
圖6及輸出:
圖5及輸出:
####三:弗洛伊德(Floyd)演算法,帶輸出路徑
和dijkstra演算法一樣,自己將這個演算法分成兩部分:
一:最短路徑長度
二:最短路徑
####首先,我們先看最短路徑長度問題
這個演算法的最短路徑長度核心程式碼只有5行
1:就是看走哪邊短!!!如果短就替換。
####其次,最短路徑(弗洛伊德輸出路徑要麻煩些)
####一:路徑的建立
####1:Path[ ][ ]初始化
矩陣Path的初值則為各個邊的終點頂點-----相當於直接從v到w(上面兩種方案的第一種)
####2:Path[ ][ ]逐漸儲存路徑
當且僅當通過“另外一個點”的時候,有更短路徑,即更新路徑Path[v][w],把“另外一個點”放進Path[v][w]裡
這裡舉個例子:比如A->D,路徑是A->E->D
####二:路徑的輸出
這裡主要是是一個迴圈,只到輸出字母是終點為止。
不防先人為的輸出起點,和第一個點,剩下的交給while迴圈
最後舉個例子:比如A->F,路徑是A->E->D->F
需要注意的是:Floyd-Warshall演算法不能解決帶有“負權迴路”(或者叫“負權環”)的圖
問題。因為帶有“負權迴路”的圖沒有最短路。比如:
相關程式碼
/*************************************************************************************************************
** 弗洛伊德(Floyd)演算法
** 核心:是一種嘗試的想法,從v到w只有兩種方案:
1:直接從v到w,即DFloyd[v][w]
2:經過“另外的一個點”u,從v到w,即DFloyd[v][u] + DFloyd[u][w]
比較兩種方案,取更小的
1:比較 :DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w]
2:取更小的:DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w]
** 這裡的Path矩陣的構造很巧,關於Path矩陣程式碼僅僅只有幾行而已!!!下面分析Path矩陣
1:矩陣Path的初值則為各個邊的終點頂點-----相當於直接從v到w(上面兩種方案的第一種)
2:當且僅當通過“另外一個點”的時候,有更短路徑,即更新路徑Path[v][w],把“另外一個點”放進Path[v][w]裡
** 路徑輸出
1:不防把起點(G.vexs[i]),和Path[i][j]先輸出(Path[i][j]是路徑的第一個點)
2:迴圈輸出直到(Path[temp][j] == j)為止!!!
** 自己可能還沒有講清楚,不用擔心我在畫個圖解釋下
請參考:https://mp.csdn.net/mdeditor/80579845
****************************************************************************************************************/
void ShortestPath_FLOYD(MGraph &G, shortestCode &Path)
{
//最短路徑長度DFloyd[][]
int **DFloyd = (int **)malloc(G.vexnum * sizeof(int *)); //動態二維陣列DFloyd[][],分配G.vexnum行
for (int i = 0; i < G.vexnum; ++i) { //為每行分配G.vexnum列
DFloyd[i] = (int *)malloc(G.vexnum * sizeof(int));
}
//最短路徑Path[][]
Path = (shortestCode)malloc(G.vexnum * sizeof(char *)); //動態二維陣列Path[][],分配G.vexnum行
for (int i = 0; i < G.vexnum; ++i) { //為每行分配G.vexnum列
Path[i] = (char *)malloc(G.vexnum * sizeof(char));
}
for (int v = 0; v < G.vexnum; v++)
for (int w = 0; w < G.vexnum; w++) {
if (v == w) //這裡把主對角線權值變為0,不然主對角線會錯,因為之前賦值的∞,舉個例子B->B,現有B->C + C->B之和肯定小於∞,所以主對角線發生錯誤
G.arcs[v][w].wight = 0;
DFloyd[v][w] = G.arcs[v][w].wight; //把初始值存入DFloyd[][]
Path[v][w] = G.vexs[w]; //矩陣Path的初值則為各個邊的終點頂點
}
printf("Path矩陣:\n");
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++)
printf("%c\t", Path[i][j]);
printf("\n");
}
/******************************************************************
** u:經過另外一個點
** v:起點
** w:終點
*******************************************************************/
for (int u = 0; u < G.vexnum; u++)
for (int v = 0; v < G.vexnum; v++)
for (int w = 0; w < G.vexnum; w++)
if (DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w]) { //比較兩種方案,取更小的
DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w];
Path[v][w] = Path[v][u]; //更新Path矩陣
}
printf("\n\t\t弗洛伊德(Floyd)演算法\nD矩陣:\n");
for (int i = 0; i < G.vexnum; i++){
for (int j = 0; j < G.vexnum; j++)
if (DFloyd[i][j] < INT__MAX)
printf("%d\t", DFloyd[i][j]);
else
printf("∞\t");
printf("\n");
}
printf("Path矩陣:\n");
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++)
printf("%c\t", Path[i][j]);
printf("\n");
}
printf("\n弗洛伊德(Floyd)最短路徑長度與最短路徑如下所示:\n");
/***************************************************************************************
** 這裡輸出除自己到自己的所有路徑
** temp:通過Path[i][j] ---找到--> Path[Path[i][j]][j]<---判斷是否相等-->j
****************************************************************************************/
for (int i = 0; i < G.vexnum; i++)
{
int temp = 0;
for (int j = 0; j < G.vexnum; j++) {
if(DFloyd[i][j] != INT__MAX)
{
if (j != i) //這裡不需要輸出自己到自己的路徑
printf("從頂點 %c ---> %c 最短路徑長度為:%d\t最短路徑為: %c %c", G.vexs[i], G.vexs[j], DFloyd[i][j], G.vexs[i], Path[i][j]); //不防把起點(G.vexs[i]),和Path[i][j]先輸出(Path[i][j]是路徑的第一個點)
}else if (DFloyd[i][j] == INT__MAX)
{
printf("從頂點 %c ---> %c 不可到達!!!", G.vexs[i], G.vexs[j]); //不存在最短路徑
}
temp = LocateVex(G, Path[i][j]);
while (temp != j) { //迴圈輸出直到(Path[temp][j] == j)為止!!!
printf(" %c", Path[temp][j]); //輸出最短路徑
temp = LocateVex(G, Path[temp][j]);
}
if (j != i) //自己到自己不需要換行
printf("\n");
}
printf("\n"); //到某個點路徑都顯示了,換行
}
free(DFloyd); //釋放DFloyd[][]空間
free(Path); //釋放Path[][]空間
}
####程式輸出
圖5及輸出:
圖6及輸出:
####主函式
#include "stdafx.h"
#include "dijkatra.h"
int main()
{
MGraph G; //有向圖的鄰接矩陣
shortestCode code; //編碼--輸出路徑
printf("Please enter vexnum and arcnum: ");
scanf("%d %d", &G.vexnum, &G.arcnum); //輸入結點數,弧數
CreatMGraph(G); //建立無向圖的鄰接矩陣
printf("\nTne output of Adjacency Matrix:\n\n");
printMatrixGraph(G); //輸出鄰接矩陣
ShortestPath_DIJ(G, G.vexs[0], code); //迪傑斯特拉(Dijkstra)演算法--求得最短路徑長度與最短路徑
ShortestPath_FLOYD(G, code); //弗洛伊德(Floyd)演算法--求得最短路徑長度與最短路徑
return 0;
}
####四:感謝與原始碼(VS2017)
1:感謝一下波主文章對我的幫助,我相信他們的文章對你也會有幫助!!!
https://blog.csdn.net/qq_34374664/article/details/52261672
https://blog.csdn.net/txl199106/article/details/44980923
2:原始碼(VS2017)
連結: https://pan.baidu.com/s/15kSoQhm2CTFHPN32OMez6g 提取碼: w8va
3:如果你覺得本文還不錯,請務必說明出處。
相關文章
- 最短路徑——Dijkstra演算法和Floyd演算法演算法
- 最短路徑—Dijkstra演算法和Floyd演算法演算法
- 最短路徑(Floyd演算法)演算法
- 單源最短路徑複習--Dijkstra演算法和Floyd演算法演算法
- 最短路徑之Floyd演算法演算法
- [MATLAB]最短路徑Floyd演算法Matlab演算法
- Floyd演算法(計算最短路徑)演算法
- 最短路徑之Dijkstra演算法演算法
- 多源最短路徑演算法:Floyd演算法演算法
- 求最短路徑——DFS+Floyd演算法演算法
- 最短路徑問題 (dijkstra演算法)演算法
- 單源最短路徑-Dijkstra演算法演算法
- 單源最短路徑 -- Dijkstra演算法演算法
- [最短路徑問題]Dijkstra演算法(含還原具體路徑)演算法
- 路徑規劃演算法 - 求解最短路徑 - Dijkstra(迪傑斯特拉)演算法演算法
- 最短路徑——floyd演算法程式碼(c語言)演算法C語言
- 圖的單源最短路徑(Dijkstra演算法)演算法
- 多源最短路徑,一文搞懂Floyd演算法演算法
- 圖 - 每對頂點間最短路徑----Floyd演算法演算法
- 最短路徑——dijkstra演算法程式碼(c語言)演算法C語言
- 最短路徑—Dijkstra(迪傑斯特拉)演算法演算法
- 0016:單源最短路徑(dijkstra演算法)演算法
- 求兩點之間最短路徑-Dijkstra演算法演算法
- 最短路演算法詳解(Dijkstra/SPFA/Floyd)演算法
- 最短路徑演算法演算法
- HDU3790 最短路徑問題【Dijkstra演算法】演算法
- 《圖論》——最短路徑 Dijkstra演算法(戴克斯特拉演算法)圖論演算法
- 一篇文章講透Dijkstra最短路徑演算法演算法
- python實現Dijkstra演算法之 最短路徑問題Python演算法
- Djikstra最短路徑演算法演算法
- (UVA - 208)Firetruck(路徑輸出問題,回溯+並查集/floyd演算法+dfs)並查集演算法
- 最短路徑(Dijskra演算法)JS演算法
- 最短路徑演算法總結演算法
- 演算法:最短路徑問題演算法
- Floyd最短路演算法演算法
- 資料結構與演算法——最短路徑Dijkstra演算法的C++實現資料結構演算法C++
- 【最短路徑Floyd演算法詳解推導過程】看完這篇,你還能不懂Floyd演算法?還不會?演算法
- 幾個最短路徑的演算法演算法