1
Floyd—Warshall算法
Floyd-warshall算法过程简述:时间复杂度O(n^3)
若A点到B点直接举例不为最短距离,则他们之间必有中间点,首先假设点K为中间点,若A->K+K->B小于A->B则将A->B更新为
A->K+K->B,遍历所有两点间的组合;
以(K+i)(1<i&&k+i<=n)点作为中间点循环时,由于两点间的距离以更新为在前K个点范围内的最短距离,所以K所在的循环结束时,任何两点间的距离为最短距离。
此算法可以处理负权边,但是可能不能处理负权回路,原因是负权回路两点间可能没有最短路径。如:1->2=2;2->3=5;3->1=-6;每循环一遍这个环,最短路径就减1.
#include <stdio.h>
#include <stdlib.h>
#define infinity 99999999
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int e[10][10],k,i,j,n,m,t1,t2,t3;
scanf("%d%d",&n,&m);//n个顶点,m条边
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)e[i][j]=0;
else e[i][j]=infinity;
}
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
//Floyd-Warshall核心算法
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(e[i][k]<infinity&&e[k][j]<infinity&&e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[k][j];
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
printf("%d->%d的最短路径:%d\n",i,j,e[i][j]);
}
}
return 0;
}
2
Djistra算法
Djistra算法简述:时间复杂度O(n^2)
Djistra算法求得是源点到各顶点间的最短路径,与Floyd算法不同(求的是各点之间的最短路径),但是后者优良的可扩展性使得它的应用更为广泛。每次循环确定一条最短的边,并更新这个顶点的出边相连的顶点到源点的距离。
#include <stdio.h>
#define inf 99999999
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i==j)e[i][j]=0;
else e[i][j]=inf;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
for(i=1;i<=n;i++)
dis[i]=e[1][i];
//book数组初始化
for(i=1;i<=n;i++)
book[i]=0;
book[1]=1;
//Dijkstra算法核心语句
for(i=1;i<=n;i++)
{
min=inf;
for(j=1;j<=n;j++)
if(dis[j]<min&&book[j]==0)
{
min=dis[j];
u=j;
}
book[u]=1;//dis[u]为确定值
for(v=1;v<=n;v++)
{
if(e[u][v]<inf)//u-v是一条u点的出边
{
if(dis[v]>dis[u]+e[u][v])
dis[v]=dis[u]+e[u][v];
}
}
}
for(i=1;i<=n;i++)
printf("1->%d:%d\n",i,dis[i]);
getchar();
return 0;
}
Bellman-Ford算法
Bellman-Ford算法简述:
此算法的时间复杂度为O(mn),比Djistra的复杂度高,但这是最差的情况,实际上在循环中可以判断是否到达了dis数组中储存的都是最短路径的地步,
可以提前退出(图中无负权回路),若有负权回路,就会运行到k=n-1,不过迪杰斯特拉也解决不了带负权边的图。
#include <stdio.h>
#define inf 99999999
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int dis[10],bak[10],i,j,k,n,m,u[10],v[10],w[10],check,flag;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d%d%d",&u[i],&v[i],&w[i]);
for(i=1;i<=n;i++)
dis[i]=inf;
dis[1]=0;
//Bellman-Ford核心语句。
for(k=1;k<=n-1;k++)//路径长度最多为通过n-个点,如果存在回路,即此回路为负权回路,无最短路径
{
check=0;
//新一轮松弛
for(i=1;i<=m;i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])
{
dis[v[i]]=dis[u[i]]+w[i];
check=1;
}
}
if(check==0)break;//数组没有更新,提前退出循环
}
//检测有无负权回路
flag=0;
for(i=1;i<=m;i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])flag=1;
}
if(flag==1)printf("此图含有负权回路\n");
else{
for(i=1;i<=n;i++)
printf("1->%d:%d\n",i,dis[i]);
}
getchar();
return 0;
}
邻接表的使用
邻接表在稀疏图的时候比邻接矩阵的使用时间复杂度更小,前者为O(),后者为O();
以下算法是邻接表的遍历应用
算法思想简述:
此算法总体思想类似于链表,但是每次插入数据是在“表头”而非“表尾”,first数组的下标表示几号顶点,存的数据表示在数组u,v,w,的第几个数据,同时(由于使用的是
next【i】)所以也同时指向了next的第i个数据;当同样的起点路线再次出现时,next【i】存入原本在first中的数据,first存入新数据;以此类推,完成类似于链表的操作,
为遍历相同起点路线提供了便利。
如果图为一个稀疏图的话,用接邻表比用接邻矩阵储存要更好,前者时间复杂度为O(M+N)log(N),后则为O(n^2)。
#include <stdio.h>
#define inf 99999999
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int n,m,i,k;
int u[6],v[6],w[6];
int first[5],next[6];
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
first[i]=-1;//初始化为-1,表示1~n顶点都没有相连的边
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);//起点,终点,两点间距离
//关键语句
next[i]=first[u[i]];
first[u[i]]=i;
}
//遍历
for(i=1;i<=n;i++)
{
k=first[i];
while(k!=-1)
{
printf("起点:%d 终点:%d 距离:%d\n",u[k],v[k],w[k]);
k=next[k];
}
}
getchar();
return 0;
}
Bellman-Ford Alogrithm(队列优化)
算法思想简述:
由Bellman-Ford算法的过程可以看出,每次松弛其实只与上次松驰过的顶点相连的边有关,所以没有必要遍历所有顶点查找可以松弛的边,使用队列将松弛过的顶点入队,
查找与顶点相连的可松弛的边,每个顶点只能入队一次 (有点像广搜啊,按照一个源点拓展,有序地遍历所有顶点)。
#include <stdio.h>
#include <string.h>
#define inf 99999999
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int dis[10],book[10]={0},i,j,k,n,m,u[10],v[10],w[10],first[10],next[10],check,flag;
scanf("%d%d",&n,&m);
memset(first,-1,sizeof(int)*10);
for(i=1;i<=m;i++)
{
scanf("%d %d %d",&u[i],&v[i],&w[i]);
next[i]=first[u[i]];
first[u[i]]=i;
}
for(i=1;i<=n;i++)
dis[i]=inf;
dis[1]=0;
int head=1,tail=1,que[101]={0};//创建队列
que[head]=1;tail++;//源点入队
while(head<tail)
{
k=first[que[head]];
while(k!=-1)
{
if(dis[v[k]]>dis[u[k]]+w[k])
{
dis[v[k]]=dis[u[k]]+w[k];
if(book[v[k]]==0)
{
que[tail]=v[k];
tail++;
book[v[k]]=1;
}
}
k=next[k];
}
book[que[head]]=0;
head++;//出队
}
for(i=1;i<=n;i++)
{
printf("1->%d:%d\n",i,dis[i]);
}
getchar();
return 0;
}