一,Dijkstra(迪杰斯特拉算法)
Dijkstra算法是求解单源最短路问题的算法。(非负权边)
Dijkstra提出,按各个顶点与源点之间路径长度的递增次序,生成源点到各个顶点的最短路径。
也就是说,先找出距离源点最近的一条路径,再参照它找出距离源点次近的一条路径,以此类推,直到从源点到其他各顶点的最短路径全部找出。
是一种贪心的思想。时间复杂度为n^2。
该算法仅适用于单源的非负权边的最短路问题。
如果存在负权边的话,就会出现错误。为什么?因为该算法是基于贪心思想的。如果存在负权会出现错误。
代码实现比较简单:
const int N=?;
const int INF=0xfffff
int map[N][N];
bool vis[N],dis[N],n;//用来区分
void Dijkstra(int s0){
memset(vis,false,sizeof(vis));
vis[s0]=true;
for(int i=1;i<=n;i++)
dis[i]=map[s0][i];
//-----------------------------
for(int i=1;i<=n;i++){
int Bfind=s0,Dis=INF
for(int j=1;j<=n;j++)
if(!vis[j]&&dis[j]<Dis){
Bfind=j;
Dis=dis[j];
}
if(Bfind==s0) break;
vis[Bfind]=true;
//-------------------------------
for(int j=1;j<=n;j++)
if(!vis[j]&&dis[j]<dis[Bfind]+map[Bfind][j]){
dis[j]=dis[Bfind]+map[Bfind][j];
}
//-------------------------------
}
printf("%d\n",dis[?]);
}
二,Bellman_ford(贝尔曼-福德算法)
Bellman_ford算法是求解一般单源最短路的算法。(可能存在负边)
算法操作就是,对图进行|V|-1次松弛操作,即可求出最短路径。
可以看出Bellman_ford的时间复杂度为|V||E|。
最重要的是理解什么是松弛操作:
理解了什么是松弛操作了,理解起来就很容易了。
为什么要进行|V|-1次松弛操作?
因为,某点到源点的距离最短,最多通过|V|-1次松弛操作可以得到。
代码很好写:
const int N=?;
const int M=?;
const int INF=0xffffff;
struct Edge{
int start,end;
int value;
}edge[M];
int n,m,dis[N],path[N];
void Bellman_ford(){
for(int i=1;i<=n;i++)
dis[i]=INF;
dis[startpoint]=0;
for(int i=1;i<n;i++){
for(int j=1;j<=m;j++){
int st=edge[j].start;
int en=edge[j].end;
int va=edge[j].value;
if(dis[en]>dis[st]+va){
dis[en]=dis[st]+va;
path[en]=st;
}
}
}
}
如果存在负权回路,那么,那么就不存在最短路,以为,每次经过负权回路都会减小。
那么,怎么判断是否存在负权回路呢?如果存在负权回路,那么,在进行|V|次以后的松弛操作,还会变小。
所以,只需要在,做完松弛操作后,判断一下,再次做松弛操作时,会不会再次减小。
代码:(加到,松弛完以后)
bool Judge(){
for(int i=1;i<=m;i++){
int st=edge[j].start;
int en=edge[j].end;
int va=edge[j].value;
if(dis[en]>dis[st]+va)
return false;
}
}
打印路径:
需要注意的是,打印路径的时候。需要注意的是,记录的是前驱,而非后继,为什么呢?
因为,一个点最短路径的前驱是唯一的,而后继可能有很多。
递归打印路径,自己写出来的竟然。自己递归还需努力。
int path[N]={'-1'};
int Print_Path(int step){
if(path[step]==-1) return step;
else Print_Path(path[one]);
printf("%s%d",step==starstep?"":"->"?one);
}
今天的复习就到这里,明天继续。