<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
body, table{font-family: 微軟雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
V0→V1 1 V0→V1→V2 4 V0→V1→V2→V4 5 V0→V1→V2→V4→V3 7 V0→V1→V2→V4→V3→V6 10 V0→V1→V2→V4→V3→V6→V7 12 V0→V1→V2→V4→V3→V6→V7→V8 16 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在求最短路徑的過程中,V0到圖中所有路徑的最短距離都計算過了一遍,只要儲存中間結果就可以在求V0→V8的最短路徑的同時得到V0到圖中任意點的距離
isInShortestPath陣列標記對應下標頂點是不是已經計算在最短路徑內了 pathPre陣列標記對應下標頂點在最短路徑的前驅結點是誰,這個結點和weight陣列對應,如下表演算法開始 weight陣列標記當前最短路徑到對應下標頂點的最短路徑,∞就是到不了了 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//演算法開始,選中V0頂點開始遍歷找最短路徑
|
//第一次遍歷找到(0,1),加入頂點1,1可以到達未加入最短路徑的頂點2,3,4; 更新陣列
//V1->V2 = 4 < 5,更新 依次類推更新weight[3] weight[4]
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//下一次遍歷就會選中V2,(1,2),加入頂點2
weight[2]+(2,4)=5
|
//加入weight最小的,且沒有加入到最短路徑中的頂點4(2,4)
weight[4] = 5,能到達3,5,6,7
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//沒加入的3,5,6,7,8中選一條最小權值的邊,選擇頂點3,(4,3)
weight[3] = 7,3只能到沒到過的6,7+(3,6)=10,weight[6]=11,更新
|
//5,6,7,8中選一個weight最小的5
weight[5]=8,5能到為加入最短路徑的7 ; weight[5]+(5,7)=13<weight[7],更新
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//沒加入的6,7,8中選一個最小的6
weight[6]=10,(6,7)=2,(6,8)=7;
|
//沒加入的7,8中選一個最小的7, weight[7]=12
weight[7]=12,(7,8)=4
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//加入最後一個結點8
|
在整個圖的遍歷搜尋中,從V0頂點到每一個其他頂點的最短距離都在這個過程中儲存在weight陣列中。 要找到兩個點之間的最短路徑,只要通過pathPre陣列找前驅就好了 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* Dijksta.cpp /
#include"Dijkstra.h"
namespace meihao
{
void ShortestPath_Dijkstra(const meihao::Graph& g,int pathPre,weight_vaule_type* weight,int vertexCnt)
{
if(nullptr==pathPre||nullptr==weight||0==vertexCnt)
return ;
//定義一個數組標記每個結點是否已經在最短路徑中
int* isInShortestPath = new intvertexCnt; //初始為0,表示都還沒有在最短路徑之中
//預設從圖中第0個結點開始,初始化weight,表示0到其他結點的最短路徑
for(int idx=0;idx!=vertexCnt;++idx)
{
weight[idx] = g.getGraphEdgeWeight(0,idx); //weight[idx]存放的就是0到idx的權值
pathPre[idx] = 0; //weight最開始用0到對應下標的距離來初始化,所以pathPre只能全部是0了
}
//計算V0到其他結點的最短路徑
isInShortestPath[0] = 1;
for(int idx=1;idx!=vertexCnt;++idx) //V0已經在路徑中,只要從下一個頂點開始
{
int min = max_weight_value;
int nextShortestPathVertex = 0; //下一個可以加到最短路徑上的頂點
//開始遍歷weight陣列找到一條從V0頂點到對應陣列小標頂點的最有最小權值的邊,並記錄邊的另一端頂點
for(int iidx=0;iidx!=vertexCnt;++iidx)
{
if(0==isInShortestPath[iidx]&&
weight[iidx]<min)
{
nextShortestPathVertex = iidx;
min = weight[iidx];
}
}
//找到了個可以加入到最短路徑的頂點
isInShortestPath[nextShortestPathVertex] = 1; //修改nextShortestPathVertex為1
//得到了一個點,通過這個點可以到一些其他頂點,這是後可能路徑又會有變化
//加入nextShortestPathVertex,更新weight陣列
for(int iiidx=0;iiidx!=vertexCnt;++iiidx)
{
/if(0==isInShortestPath[iiidx]&&
(weight[nextShortestPathVertex]+g.getGraphEdgeWeight(nextShortestPathVertex,iiidx))<weight[iiidx]) /
//這裡錯誤的原因是有的weigth是weight_vaule_type能表示的最大值,再加就溢位了
if(0==isInShortestPath[iiidx] &&
g.getGraphEdgeWeight(nextShortestPathVertex,iiidx) !=max_weight_value && //多一個條件,防止溢位,也就是(nextShortestPathVertex,iiidx)之間有邊的頂點
(weight[nextShortestPathVertex]+g.getGraphEdgeWeight(nextShortestPathVertex,iiidx) ) < weight[iiidx] )
//如果新加入最短路徑的點到其他沒有標記到最短路徑中的點idx的權值小於之前某點到idx的權值,就可以更新這個權值
{
weight[iiidx] = weight[nextShortestPathVertex]+g.getGraphEdgeWeight(nextShortestPathVertex,iiidx);
pathPre[iiidx] = nextShortestPathVertex;
}
}
}
}
void printShortestPath(int vi,int vj,int* pathPre,int vertexCnt)
{
if(nullptr==pathPre||0==vertexCnt||vi<0||vj<0||vi>vj||vi>=vertexCnt||vj>=vertexCnt)
return ;
if(vi==vj)
{
cout<<vi<<" ";
return ;
}
printShortestPath(vi,pathPre[vj],pathPre,vertexCnt);
cout<<vj<<" ";
}
};
|
/* Dijkstra.h /
#ifndef DIJKSTRA_H
#define DIJKSTRA_H
#include"Graph.h"
namespace meihao
{
void ShortestPath_Dijkstra(const meihao::Graph& g,int pathPre,weight_vaule_type* weight,int vertexCnt); //引數pathPre陣列是用來存放計算得到到圖中某一點的最短路徑前驅
//pathPre[2] = 1,表示V2到V1的最短路徑中,V2前面一個點是V1, ...V1->V2...
//weight是存放指定的一個開始遍歷的到對應陣列小標的點的權值。eg:圖中起始的點V0,weight[2] = N,表示V0到V2的最短路徑權值和為N
//定義一個函式輸出指定兩點之間的最短路徑和權值和
void printShortestPath(int vi,int vj,int* pathPre,int vertexCnt);
};
#endif
/* testmain.cpp /
#include"Graph.h"
#include<iostream>
#include"Dijkstra.h"
using namespace std;
int main()
{
meihao::Graph g("data.txt");
int vertexCnt = g.getGraphVertexNumber();
int pathPre = new intvertexCnt;
weight_vaule_type* weight = new weight_vaule_typevertexCnt;
meihao::ShortestPath_Dijkstra(g,pathPre,weight,vertexCnt);
for(int idx=0;idx!=vertexCnt;++idx)
{
meihao::printShortestPath(0,idx,pathPre,vertexCnt);
cout<<"路徑權值:"<<weight[idx];
cout<<endl;
}
cout<<endl;
cout<<"圖的起始頂點0到終點8的最短路徑:";
meihao::printShortestPath(0,8,pathPre,vertexCnt);
cout<<" 路徑權值:"<<weight[8]<<endl;
delete []pathPre;
delete []weight;
system("pause");
}
/* data.txt /
9
0 1 2 3 4 5 6 7 8
0 1 5 -1 -1 -1 -1 -1 -1
1 0 3 7 5 -1 -1 -1 -1
5 3 0 -1 1 7 -1 -1 -1
-1 7 -1 0 2 -1 3 -1 -1
-1 5 1 2 0 3 6 9 -1
-1 -1 7 -1 3 0 -1 5 -1
-1 -1 -1 3 6 -1 0 2 7
-1 -1 -1 -1 9 5 2 0 4
-1 -1 -1 -1 -1 -1 7 4 0
|
圖中D-1矩陣是存放的圖的臨界矩陣,對應的P-1矩陣是存放對應頂點的前驅頂點矩陣。 eg:p[V1][V0]=0,(V1,V0),只能是0; p[V1][V1]=1,(V1,V1); p[V1][V2]=2,(V1,V2) 這是 初始,現以V0為中間頂點更新兩個矩陣: (V1,V0)不變,(V1,V1)不變,(V1,V2)->V1,V0,V2 = 3,之前為5,更新 所以更新兩個矩陣,p[V1][V2]=0,表示(V1,V2)最短路徑中V2前一個結點是V0,對應的D[V1][V2]=3。這時候的矩陣為P0,D0。
| |
//以V0為中間頂點,所有點(Vi,Vj)經過V0求路徑,確定是否要更新 |
|
//以V1為中間頂點,所有點(Vi,Vj)經過V1求路徑,確定是否要更新 |
//最終結果,D矩陣中(Vi,Vj)就是Vi,Vj之間的最短路徑 //P矩陣中,P[V0][V8]=1表示V0->V8要先經過V0->V1->...->V8,P[V1][V8]=2 ... P[V8][V8]=8 |
/ Floyd.h */
#ifndef FLOYD_H
#define FLOYD_H
#include"Graph.h"
#include<iostream>
namespace meihao
{
void ShortestPath_Floyd(const meihao::Graph& g,weight_vaule_type weight,int pathPre);
//weight相當於D陣列,pathPre相當於P陣列
void printPath(int vi,int vj,int vertexCnt,int** pathPre);
//列印vi到vj的最短路徑
};
#endif
/* testmain.cpp */
#include"Graph.h"
#include<iostream>
#include"Floyd.h"
#include<iomanip>
using namespace std;
int main()
{
cout<<"test Floyd:"<<endl;
meihao::Graph g("data.txt");
int vertexCnt = g.getGraphVertexNumber();
int** pathPre = new int*vertexCnt;
for(int idx=0;idx!=vertexCnt;++idx)
pathPre[idx] = new intvertexCnt;
weight_vaule_type** weight = new weight_vaule_typevertexCnt;
for(int idx=0;idx!=vertexCnt;++idx)
weight[idx] = new weight_vaule_type[vertexCnt];
meihao::ShortestPath_Floyd(g,weight,pathPre);
cout<<"print weight matrix:"<<endl;
for(int idx=0;idx!=vertexCnt;++idx)
{
for(int iidx=0;iidx!=vertexCnt;++iidx)
{
cout<<setw(3)<<weight[idx][iidx]<<" ";
}
cout<<endl;
}
cout<<endl;
cout<<"print pathPre matrix:"<<endl;
for(int idx=0;idx!=vertexCnt;++idx)
{
for(int iidx=0;iidx!=vertexCnt;++iidx)
{
cout<<setw(3)<<pathPre[idx][iidx]<<" ";
}
cout<<endl;
}
cout<<endl;
for(int idx=0;idx!=vertexCnt;++idx)
{
meihao::printPath(0,idx,vertexCnt,pathPre);
cout<<"路徑權值: "<<weight[0][idx]<<endl;
}
//釋放動態記憶體
for(int idx=0;idx!=vertexCnt;++idx)
{
delete []pathPre[idx];
pathPre[idx] = nullptr;
delete []weight[idx];
weight[idx] = nullptr;
}
delete []pathPre;
pathPre = nullptr;
delete []weight;
weight = nullptr;
system("pause");
}
|
/ Floyd.cpp */
#include"Floyd.h"
#include<iostream>
namespace meihao
{
void ShortestPath_Floyd(const meihao::Graph& g,weight_vaule_type weight,int pathPre)
{
if(nullptr==weight||nullptr==pathPre)
return ;
//讀取圖中陣列初始化weight和pathPre
int vertexCnt = g.getGraphVertexNumber();
for(int idx=0;idx!=vertexCnt;++idx)
{
for(int iidx=0;iidx!=vertexCnt;++iidx)
{
weight[idx][iidx] = g.getGraphEdgeWeight(idx,iidx);
pathPre[idx][iidx] = iidx;
}
}
//開始依次選擇中間結點,更新weight和pathPre陣列
//中間結點為0時和初始化的矩陣一樣
for(int idx=1;idx!=vertexCnt;++idx)
{
for(int v=0;v!=vertexCnt;++v)
{
for(int w=0;w!=vertexCnt;++w)
{
if( (max_weight_value!=weight[v][idx] && max_weight_value!=weight[idx][w]) && //經過中間結點idx,(v,idx)和(idx,w)都不能是最大值,不然下邊的相加判斷會溢位
//max_weight_value是權值weight_vaule_type所能表示的最大值了,再加就溢位
weight[v][w]>(weight[v][idx] + weight[idx][w]) )
{//更新
weight[v][w] = weight[v][idx] + weight[idx][w];
pathPre[v][w] = pathPre[v][idx]; //pathPre[v][w] = idx,也就是(v,w)要先到達idx,但是(v,idx)不是一步就到達的,也要先經過pathPre[v][idx]
//pathPre[v][w] = idx; //不能寫這個,要在前面得到的中間結果的基礎之上
}
}
}
}
}
void printPath(int vi,int vj,int vertexCnt,int** pathPre)
{
if(vi<0||vj<0||vertexCnt<=0||nullptr==pathPre)
return ;
if(vi==vj)
{
cout<<vi<<" ";
return ;
}
int max = vi>=vj?vi:vj;
int min = vi<vj?vi:vj;
cout<<min<<" "; //從最小的起始點開始
while(min!=max)
{
cout<<pathPre[min][max]<<" "; //第一個要經過的點
min = pathPre[min][max]; //更新起始頂點
}
}
};
|