单源最短路径 -- Dijkstra算法

求一个点(源点)到其余点的最短路径问题,利用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算法,这是加入了邻接表,使算法的时间复杂度降低。邻接表其实就是减少了存储边的空间,把具有相同起点的边“串”了起来,这里的“串”就是通过head数组+edge[i].next实现的

 

建图的空间复杂度为O(m),遍历每一条边的时间复杂度为O(m),总的时间复杂度降为O(n*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; //next的值是以该条边为起点的边的上一条边在存放边的数组中的位置
};
node edge[maxn];//存储边的信息数组
int book[maxn];//标记一下该条边是否已经确定是最小边了
int head[maxn];//存放以i为起点的这条边在存放边的数组中的位置
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];//这里其实是一个倒着的过程,next其实是指的上一条边
        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)

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值