Floyd算法
Floyed 算法与 Dijkstra 算法的思想完全一样,遍历整个邻接矩阵,比较每一条边可否加入循环中的两边之间来短接。
Floyed 算法基于动态规划算法,可以允许有负权值,但不可以有带负权值的边存在。
首先,初始化一个邻接矩阵,初始化一个 dp 矩阵,初始化一个顶点的前趋矩阵prev。
#define N 6
vector<vector<int> > Graph(N,vector<int>(N,INT_MAX)); //邻接矩阵
vector<vector<int> > dp(N,vector<int>(N,INT_MAX));//dp数组
vector<vector<int> > prev(N,vector<int>(N,INT_MAX));
void Floyd(){
//prev矩阵初始化
for(int i = 0;i < N;i ++)
for(int j = 0; j < N;j ++)
if(i != j && dp[i][j] < INT_MAX) prev[i][j] = i;
else prev[i][j] = INT_MAX;
for(int k = 0;k < N;k ++){
for(int i = 0;i < N;i ++)
for(int j = 0; j < N;j ++)
if(dp[i][k] < INT_MAX && dp[k][j] < INT_MAX && dp[i][k]+dp[k][j] < dp[i][j]){ //k可以短接到i和j之间
dp[i][j] = dp[i][k]+dp[k][j]; //更新
prev[i][j] = prev[k][j]; //更新
}
}
}
全部代码如下:
#include<iostream>
#include<vector>
using namespace std;
#define N 6
vector<vector<int> > Graph(N,vector<int>(N,INT_MAX)); //邻接矩阵
vector<vector<int> > dp(N,vector<int>(N,INT_MAX));//dp数组
vector<vector<int> > prev(N,vector<int>(N,INT_MAX));
void Floyd(){
//prev矩阵初始化
for(int i = 0;i < N;i ++)
for(int j = 0; j < N;j ++)
if(i != j && dp[i][j] < INT_MAX) prev[i][j] = i;
else prev[i][j] = INT_MAX;
for(int k = 0;k < N;k ++){
for(int i = 0;i < N;i ++)
for(int j = 0; j < N;j ++)
if(dp[i][k] < INT_MAX && dp[k][j] < INT_MAX && dp[i][k]+dp[k][j] < dp[i][j]){ //k可以短接到i和j之间
dp[i][j] = dp[i][k]+dp[k][j]; //更新
prev[i][j] = prev[k][j]; //更新
}
}
}
int main(){
//邻接矩阵
Graph[0][2] = 10;
Graph[2][0] = 10;
Graph[0][4] = 30;
Graph[4][0] = 30;
//测试带负环运行floyd算法
// Graph[0][4] = -30;
// Graph[4][0] = -30;
Graph[0][5] = 100;
Graph[5][0] = 100;
Graph[1][2] = 5;
Graph[2][1] = 5;
Graph[2][3] = 50;
Graph[3][2] = 50;
Graph[3][5] = 10;
Graph[5][3] = 10;
Graph[4][3] = 20;
Graph[3][4] = 20;
Graph[4][5] = 60;
Graph[5][4] = 60;
for(int i = 0;i < N;i ++)
for(int j = 0; j < N;j ++){
dp[i][j] = Graph[i][j];
}
cout<<"邻接矩阵:"<<endl;
for(int i = 0;i < N;i ++){
for(int j = 0; j < N;j ++)
if(dp[i][j] == INT_MAX) cout<<"∞ ";
else cout<<dp[i][j]<<" ";
cout<<endl;
}
cout<<"Floyd算法:"<<endl;
Floyd();
for(int i = 0;i < N;i ++){
for(int j = 0; j < N;j ++)
if(i == j) cout<<"∞ ";
else if(dp[i][j] == INT_MAX) cout<<"∞ ";
else cout<<dp[i][j]<<" ";
cout<<endl;
}
cout<<"prev数组:"<<endl;
for(int i = 0;i < N;i ++){
for(int j = 0; j < N;j ++)
if(i == j) cout<<"∞ ";
else if(prev[i][j] == INT_MAX) cout<<"∞ ";
else cout<<"v"<<prev[i][j]+1<<" ";
cout<<endl;
}
}
运行结果:
dp 数组的第一行的结果和从 v1 顶点开始的 Dijkstra 算法是完全相同的。
Dijkstra 算法基于贪心策略。
Dijkstra 算法运行结果:
全部代码:
#include<iostream>
#include<vector>
using namespace std;
#define N 6
vector<vector<int> > Graph(N,vector<int>(N,INT_MAX));//邻接矩阵
vector<bool> visited(N,false); //访问标记数组
vector<int> dis(N,INT_MAX); //距离数组
vector<int> prev(N,INT_MAX); //前趋矩阵
vector<int> res;
void Dijkstra(int v){
dis[v] = 0; //v开始
while(true){
int v = -1;
for(int u = 0;u < N;u ++)
if(!visited[u] && (v == -1 || dis[u] < dis[v])) v = u;
if(v == -1) break; //搜索完毕
visited[v] = true;
cout<<"v"<<v+1<<"加入点集:"<<endl;
res.push_back(v);
for(int u = 0;u < N;u ++)
if(Graph[v][u] < INT_MAX && dis[u] > dis[v]) { //u可以短接
dis[u] = min(dis[u],dis[v] + Graph[v][u]);
prev[u] = v; //记录u的前趋
}
for(int i = 0;i < N;i ++){
if(dis[i] == INT_MAX) cout<<"∞ "<<" ";
else cout<<dis[i]<<" ";
}
cout<<endl;
}
}
int main(){
Graph[0][2] = 10;
Graph[2][0] = 10;
Graph[0][4] = 30;
Graph[4][0] = 30;
// Graph[0][4] = -30;
// Graph[4][0] = -30;
Graph[0][5] = 100;
Graph[5][0] = 100;
Graph[1][2] = 5;
Graph[2][1] = 5;
Graph[2][3] = 50;
Graph[3][2] = 50;
Graph[3][5] = 10;
Graph[5][3] = 10;
Graph[4][3] = 20;
Graph[3][4] = 20;
Graph[4][5] = 60;
Graph[5][4] = 60;
int v = 0;
Dijkstra(v);
cout<<endl<<"最短路径:"<<endl;
for(int i = 0;i < N;i ++){
if(res[i] == INT_MAX) cout<<"∞ "<<" ";
else cout<<"v"<<res[i] + 1<<" ";
}
cout<<endl<<"前趋矩阵:"<<endl;
for(int i = 0;i < N;i ++){
if(prev[i] == INT_MAX) cout<<"∞ "<<" ";
else cout<<"v"<<prev[i] + 1<<" ";
}
}
Floyed 算法的时间复杂度O(N*N*N)。
Dijkstra 算法的时间复杂度O(N*N)。Dijkstra 算法中求最小边的部分可以构造一个堆来实现,时间复杂度就降到了O(N*logN)。