您的位置:首页 > 其它

单源最短路径 -- Dijkstra算法

2016-11-04 21:26 357 查看
求一个点(源点)到其余点的最短路径问题,利用f数组记录该节点的父节点,以便以后打印路经的时候,倒着回去的时候打印路径

#include<iostream>        //没有利用邻接表存储图形结构的算法,该种方法存储图的空间复杂度为O(n*n),遍历每一条边的时间复杂度为O(m)
#include<cstdio>          //算法的时间复杂度为O(n*n)
#include<cstdlib>
#include<cstring>         //有缺点,就是没法解决带有权值是负值的边
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#include<stack>
#define INF 99999999
#define MAX 20
using namespace std;

int dis[MAX];
int city[MAX][MAX];
int book[MAX];//标记数组 ,为1代表该 dis[i]为确定的值,也就是真的最短路径,0代表不确定该路径是不是最短路径
int f[MAX];//用来存放该节点的父节点是谁
stack <int> a, b;

int main()
{
int n,m,node;
scanf("%d%d",&n,&m);//n点的个数,m边的条数

//建立城市之前的关系图,自身到自身距离为0,两个城市无法到达则为INF
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j)
city[i][j]=0;
else city[i][j]=INF;
}
int x,y,r;
//输入题目给出的两个城市之间的距离
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&r);
city[x][y]=r;
}
scanf("%d",&node);//输入要求的那个点,求该点到其他点的最短路径

//初始化dis数组,里面存放的是一开始,node点到其余各点的路径
for(int i=1;i<=n;i++) {
dis[i]=city[node][i];
if(dis[i] < INF)
f[i] = 1;
}

//初始化book数组,其实可以把数组定义main函数外面(即book数组是全局变量),book数组里面的每个元素都是0
memset(book,0,sizeof(book));
book[node]=1;//自己到本身的距离是0,确定的,所以一开始就把book[node]标记为确定值

//Dijkstra算法的核心语句
for(int t=1;t<n;t++)//一共需要计算多少次,因为当算到最后一个点的时候,没有其他选择了,
{                   // 该点的dis[i]就是最短路径,确切值
int Min=INF,temp;
for(int i=1;i<=n;i++)
{
if(book[i]==0 && dis[i]<Min)
{
Min=dis[i];
temp=i;
}
}
book[temp]=1;
for(int i=1;i<=n;i++)
{
if(!book[i] && city[temp][i]<INF)
{
if(dis[i]>dis[temp]+city[temp][i])//dis[i]代表从1号顶点到i号定点的起始最短路径,如果经过temp点一转后
dis[i]=dis[temp]+city[temp][i];//路径变短了,则更新这两点之前的最短路径(此时的最短路径
f[i] = temp;
}                                      // 不一定是最短的),已经被book标记的dis[i]才是最短路径,即book[i]=1
}
}

//打印出缩短后的路径,city[i][j]代表i点到j点的最短距离
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
printf("\n");

int i = f[6];
a.push(6);
while(i != 1) {
a.push(i);
i = f[i];
}
printf("从1点到6点的最短路径的走法:\n");
printf("1 ");
while(!a.empty()) {
printf("%d ",a.top());
a.pop();
}
printf("\n");
return 0;
}
/*测试数据
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
1

程序运行效果
0 1 8 4 13 17
从1点到6点的最短路径的走法:
1 2 4 3 5 6
*/


(2)利用邻接表来实现Dijkstra算法,这是加入了邻接表,是算法的时间复杂度降低

建图的空间复杂度为O(m),遍历每一条边的时间复杂度为O(m)

#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define inf 0x3f3f3f3f
#define maxn 20
using namespace std;
struct node{
int to;
int w;
int next;
};
node edge[maxn];//存储边的信息数组
int book[maxn];//标记一下该条边是否已经确定是最小边了
int head[maxn];//存放i节点的第一条边在edge数组里面的位置
int dis[maxn];//存放num点到每个点的最短距离
int n,m,num;//num为要求的点,即该点到其他点的最短距离

int main()
{
scanf("%d%d%d",&n,&m,&num);
memset(head,-1,sizeof(head));//初始化为-1的是head数组
memset(dis, inf, sizeof(dis));
memset(book, 0,sizeof(book));
for(int i=0;i<m;i++)//邻接表的建立过程
{
int front;
scanf("%d%d%d",&front,&edge[i].to,&edge[i].w);
edge[i].next=head[front];
head[front]=i;
}
int k=head[num];//以num为节点,遍历与它相连的每一条边
dis[num]=0;//到本身的距离为0
book[num]=1;
while(k!=-1)//也算是对dis数组初始化的一部分
{
dis[edge[k].to]=edge[k].w;
k=edge[k].next;
}

for(int i=1;i<n;i++)//一共要计算n-1个点,因为剩下最后一个点后,该点的dis距离就是最短的
{
int Min=inf,t;
for(int j=1;j<=n;j++)//遍历dis数组里面的每一条边,找出离num点最近的点
{
if(book[j]==0 && Min>dis[j])
{
Min=dis[j];
t=j;
}
}
book[t]=1;
int l=head[t];
while(l!=-1)//遍历t点所连接的每一条边,看能否松弛
{
//edge[l].w代表第l条边的权值,dis[t]代表num点到t点的最短距离
//edge[l].to代表第l条边的终点,dis[edge[l].to]代表num点到edge[l].to的距离
if(!book[edge[l].to] && edge[l].w+dis[t]<dis[edge[l].to])
dis[edge[l].to]=edge[l].w+dis[t];
l=edge[l].next;//l边的下一条边,即与t点相连的下一条边
}
}
printf("\n");
for(int i=1;i<=n;i++)
{
printf("%d ",dis[i]);
}
printf("\n");

return 0;
}
/*
输入数据
6 9 1
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4

输出结果
0 1 8 4 13 17
*/

(3)继续对上面的第二种算法进行优化,在判断离dis数组最近距离的点的时候利用堆排序,是该部分算法的时间复杂度由O(N)降低到O(logN)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: