在上一篇数据结构与算法分析-C++描述 第9章 图论算法(单源最短路径问题之Dijkstra算法邻接表实现)中介绍了贪心算法的经典代表Dijkstra算法。该算法是非负单源路径最短问题的极好解决方案,在使用优先队列时,每一次查找最小值使用时间,因此算法的最终时间复杂度为。在使用斐波拉契堆(后续学习中会见到该堆的实现)时,其算法的最终时间复杂度为。整体上讲,Dijkstra算法具有良好的算法性能。但是对于有负值权重的图时,往往得不到正确的结果。如下图所示:
使用Dijkstra算法从1到6的最短路径为15【显然,从1-> 3 -> 4 -> 6 存在最短路径10】。此时的dist数组的结果见下图(推导过程见上篇)。
由此引出Floyd算法。该算法能解决负值权重问题的同时,给出了所有通路的最短路径。该算法的核心表达式为:
即i到j是否存在一个中间节点k使得从i到j的路径最短,若存在则更新。
算法行为描述:
step 1 :初始化邻接矩阵:
1 | 2 | 3 | 4 | 5 | 6 | |
1 | inf | inf | 10 | inf | 30 | 15 |
2 | inf | inf | 20 | inf | inf | inf |
3 | inf | inf | inf | 10 | inf | inf |
4 | inf | inf | inf | inf | 15 | -10 |
5 | inf | inf | inf | inf | inf | -10 |
6 | inf | inf | inf | inf | inf | inf |
step 2 初始化距离矩阵:
1 | 2 | 3 | 4 | 5 | 6 | |
1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | 2 | 2 | 2 | 2 | 2 | 2 |
3 | 3 | 3 | 3 | 3 | 3 | 3 |
4 | 4 | 4 | 4 | 4 | 4 | 4 |
5 | 5 | 5 | 5 | 5 | 5 | 5 |
6 | 6 | 6 | 6 | 6 | 6 | 6 |
step 3 更新距离矩阵:(以1 -> 5为例:dist[1][5] = 30, dist[1][2]不存在,(dist[1][3], dist[3][4], dist[4][5]) = 35>30 ,所以dist[1][5] = 30,其他推理逐步进行,因此可以知道Floyd算法的时间复杂度为)
1 | 2 | 3 | 4 | 5 | 6 | |
1 | 0 | inf | 10 | 20 | 30 | 10 |
2 | inf | 0 | 20 | 30 | 45 | 20 |
3 | inf | inf | inf | 10 | inf | inf |
4 | inf | inf | inf | inf | 15 | -10 |
5 | inf | inf | inf | inf | inf | -10 |
6 | inf | inf | inf | inf | inf | inf |
Floyd算法实例:使用邻接表实现Floyd算法,打印邻接表信息,求解所有节点之间的最短路径。
//main.cpp
#include<iostream>
#include <stack>
using namespace std;
const int MAX_NUM = 20;
const int INF = 99999;
struct Graph{
int *edage[MAX_NUM];
int numVertex;
int numEdage;
};
//create Graph g
void createGraph(Graph &g);
//floyd algorithm
void floyd(Graph g);
//print the result
void print(Graph g);
int *arrayDist[MAX_NUM];
int main(){
for(int i = 0; i < MAX_NUM; i++){
arrayDist[i] = new int[MAX_NUM];
}
Graph g;
for(int i = 0; i <= MAX_NUM; i++){
g.edage[i] = new int[MAX_NUM];
}
createGraph(g);
floyd(g);
print(g);
return 0;
}
void createGraph(Graph &g){
cout << "Please enter the numVertex and numEdage splicted with space : ";
cin >> g.numVertex >> g.numEdage;
for(int i = 1; i <= g.numVertex; i++){
for(int j = 1; j <= g.numVertex; j++){
g.edage[i][j] = INF;
}
}
cout << "E(v_i, v_j, w) means v_i -> v_j with weight w " << endl;
int v_i, v_j, w;
for(int i = 1; i <= g.numEdage; i++){
cout << "Please enter the E(v_i, v_j, w) : ";
cin >> v_i >> v_j >> w;
g.edage[v_i][v_j] = w;
}
}
void floyd(Graph g){
for(int i = 0; i <= g.numVertex; i++){
for(int j = 0; j <= g.numVertex; j++){
arrayDist[i][j] = i;
}
}
for(int k = 1; k <= g.numVertex; k++){
for(int i = 1; i <= g.numVertex; i++){
for(int j = 1; j <= g.numVertex; j++){
if(g.edage[i][j] > g.edage[i][k] + g.edage[k][j]){
g.edage[i][j] = g.edage[i][k] + g.edage[k][j];
arrayDist[i][j] = arrayDist[k][j];
}
}
}
}
}
void print(Graph g){
cout << "start -> end \t Distance \t Path" << endl;
for(int i = 1; i <= g.numVertex; i++){
for(int j = 1; j <= g.numVertex; j++){
if(i != j){
cout << i << " -> " << j << " \t \t";
if((g.edage[i][j] >= INF/10 ) ){
cout << "Unconnected " << " \t \t" << endl;
}else{
cout << g.edage[i][j] << " \t \t";
stack<int> temp;
int k = j;
do{
k = arrayDist[i][k];
temp.push(k);
}while(k != i);
cout << temp.top();
temp.pop();
int nSize = temp.size();
for(int index = 0; index < nSize; index++){
cout << " -> " << temp.top();
temp.pop();
}
cout << " -> " << j << endl;
}
}
}
}
}
实验结果:
practice makes perfect !