今天來看看圖的最短路徑。
最短路徑在許多方面都有廣泛的應用,最短路徑中的路徑一般不再是路徑上邊的數目,而是路徑邊上的權值之和。所以最短路徑都是用在帶權有向圖上面的。在最短路徑方面,有兩個著名的算法,Dijkstra算法和Floyd算法。
1.Dijkstra算法-從某個點到其餘各點的最短路徑
Dijkstra算法使用了貪心算法思想,主要特點是以起始點爲中心向外層層擴展,直到擴展到終點爲止。但是注意該算法要求圖中不存在負權邊。因爲單純講太過空虛,所以我直接用一個例子來講解。下面是一個帶權有向圖的示例。
此時他的帶權鄰接矩陣爲(鄰接矩陣的定義在傳送門):
接下來根據示例圖和鄰接矩陣來講解Dijkstra算法是如何求得最短路徑的。
1. 一開始我們初始化已選擇點集S,我們假設源點爲
2. 我們首先遍歷除
3. 接下來再一次遍歷除S內元素之外的所有點。我們可以知道,如果想知道
v0 到vk 上的權值。- 或者是之間經歷了其他頂點,假設經歷了一個頂點
vj ,則最短距離 =v0 到vj 的最短路徑 +vj 到vk 上的權值。而這個vj 就是我們加入到集合S裏面的頂點。
所以我們開始更新第一次的路徑長度結果,發現通過新加入的點(即
4.再次按照3中的形式進行遍歷S之外的點。 此時我們看到加入了
5. 再次按照3中的形式進行遍歷S之外的點。更新了
接下來是主要的算法代碼,在這裏我們使用的圖存儲結構依然是鄰接矩陣。輸入的形式是以鄰接點對的形式,以0 0 0結束。在代碼裏,我們看到主程序就是一個嵌套循環,所以它的時間複雜度是O(
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define MAX 100
#define INT_MAX 10000
#define max(x,y)(x>y?x:y)
typedef struct{
int matrix[MAX][MAX]; //鄰接矩陣
int num;// 最大的頂點數
}Graph;
int initNode=0;
int *shortPath= NULL ;//最短距離
int *S=NULL;
Graph example; //實例圖
// 初始化圖
void initGraph(){
int i,j;
for(i=0;i<MAX;i++){
for(j=0;j<MAX;j++){
example.matrix[i][j]=INT_MAX;
}
}
example.num=0;
}
// 創建圖
void createPoint(){
int i,j,val;
initGraph();
while(cin>>i>>j>>val){
if(i==0 && j==0) break;
example.matrix[i][j]=val;
example.num = max(example.num,i);
//這裏偷了個懶,我這裏假設頂點之間序號是連續的,沒有點之間
//序號跨很大的情況,如果需要的話,大家可以寫一個函數改一下
//這裏。
example.num = max(example.num,j);
}
}
//輸出最後的最短路徑
void printResult(){
cout<<"--------------------------"<<endl;
for(int i=0;i<=example.num;i++){
if(i!=initNode)cout<<initNode<<"->"<<i<<":"<<shortPath[i]<< " "<<endl;
}
}
//dijkstra算法
void dijkstra(){
int i,j;
shortPath = new int[example.num+1];
S = new int[example.num+1];
int count = 0; //記錄S內元素個數
int nowProP; //記錄現在處理的頂點
int minPoint,minval;
for(i=0;i<=example.num;i++){
S[i]=-1;
shortPath[i]=INT_MAX;
}
S[initNode] = 1;
count++;
nowProP = initNode;
//兩層循環
while(count<=example.num+1){
minPoint = -1,minval= INT_MAX+1;
for(i=0;i<=example.num;i++){
if(S[i]!=1){
if(example.matrix[initNode][i]< shortPath[i]){
shortPath[i] = example.matrix[initNode][i];
}
if(example.matrix[nowProP][i]+shortPath[nowProP] < shortPath[i]){
shortPath[i] = example.matrix[nowProP][i]+shortPath[nowProP];
}
if(shortPath[i]<minval){minval=shortPath[i];minPoint=i;}
}
}
S[minPoint] = 1;nowProP=minPoint;
count++;
}
}
int main(){
initNode = 0; //這裏我們假設源點是v0,也可以改成其他的.
createPoint();
dijkstra();
printResult();
return 0;
}
最後結果是:
0 2 10
0 4 30
0 5 100
1 2 5
2 3 50
3 5 10
4 5 60
4 3 20
0 0 0
--------------------------
0->1:10000
0->2:10
0->3:50
0->4:30
0->5:60
2.Floyd算法-每對點之間的最短路徑
Floyd算法主要的是動態規劃的思想(下面的介紹來自於這篇博客)。
從任意節點i到任意節點j的最短路徑不外乎2種可能,1是直接從i到j,2是從i經過若干個節點k到j。所以,我們假設Dis(i,j)爲節點u到節點v的最短路徑的距離,對於每一個節點k,我們檢查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,證明從i到k再到j的路徑比i直接到j的路徑短,我們便設置Dis(i,j) = Dis(i,k) + Dis(k,j),這樣一來,當我們遍歷完所有節點k,Dis(i,j)中記錄的便是i到j的最短路徑的距離。
算法描述:
1. 從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權爲無窮大。
2. 對於每一對頂點 u 和 v,看看是否存在一個頂點 w 使得從 u 到 w 再到 v 比己知的路徑更短。如果是更新它。
我覺得上面的介紹已經基本上差不多了,下面是一個帶權有向圖和它的鄰接矩陣。
下面是該例子的講解圖。
D(−1) : 鄰接矩陣的初始狀態。D(0) : 此時我們對於節點0,檢查Dis(i,0) + Dis(0,j) < Dis(i,j)是否成立。 循環後我們發現Dis(2,0) + Dis(0,1) = 7 < Dis(2,1) =∞ ,所以替換掉 Dis(2,1)=7。D(1) : 按照2裏面的方法,我們發現Dis(0,1) + Dis(1,2) = 6 < Dis(0,2) = 11。替換掉Dis(0,2) 。重複該步驟,直到循環結束。
而根據圖我們可以知道這個主程序是三層循環,所以時間複雜度爲O(
接下來是主要的算法代碼,在這裏我們使用的圖存儲結構依然是鄰接矩陣。輸入的形式是以鄰接點對的形式,以0 0 0結束。輸入例子就是這個圖。此時我們注意這個鄰接矩陣的對角線需要處理成0,而不是
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define MAX 100
#define INT_MAX 10000
#define max(x,y)(x>y?x:y)
typedef struct{
int matrix[MAX][MAX]; //鄰接矩陣
int num;// 最大的頂點數
}Graph;
Graph example; //實例圖
// 初始化圖
void initGraph(){
int i,j;
for(i=0;i<MAX;i++){
for(j=0;j<MAX;j++){
example.matrix[i][j]=INT_MAX;
}
example.matrix[i][i]=0;
}
example.num=0;
}
// 創建圖
void createPoint(){
int i,j,val;
initGraph();
while(cin>>i>>j>>val){
if(i==0 && j==0) break;
example.matrix[i][j]=val;
example.num = max(example.num,i);
//這裏偷了個懶,我這裏假設頂點之間序號是連續的,沒有點之間
//序號跨很大的情況,如果需要的話,大家可以寫一個函數改一下
//這裏。
example.num = max(example.num,j);
}
}
//輸出最短路徑矩陣
void printResult(){
cout<<"--------------------------"<<endl;
for(int i=0;i<=example.num;i++){
for(int j=0;j<=example.num;j++){
cout<<example.matrix[i][j]<<" ";
}
cout<<endl;
}
}
//Floyd算法
void Floyd(){
int i,j,k;
for(k=0;k<=example.num;k++){
for(i=0;i<=example.num;i++){
for(j=0;j<=example.num;j++){
if(example.matrix[i][k]+example.matrix[k][j]<example.matrix[i][j]){
example.matrix[i][j] = example.matrix[i][k]+example.matrix[k][j];
}
}
}
}
}
int main(){
createPoint();
Floyd();
printResult();
return 0;
}
最後輸出結果:
0 1 4
0 2 11
1 0 6
1 2 2
2 0 3
0 0 0
--------------------------
0 4 6
5 0 2
3 7 0