Dijkstra(迪杰斯特拉)算法FreePascal及c++实现

Dijkstra(迪杰斯特拉)算法

简介

  Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算
法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路
径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。Dijkstra一般的表述通常有两种方式,一种用永久和临时标号
方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。

 

算法描述

  (这里描述的是从节点1开始到各点的dijkstra算法,其中Wa->b表示a->b的边的权值,d(i)即为最短路径值)
  1. 置集合S={2,3,...n}, 数组d(1)=0, d(i)=W1->i(1,i之间存在边) or +无穷大(1.i之间不存在边)
  2. 在S中,令d(j)=min{d(i),i属于S},令S=S-{j},若S为空集则算法结束,否则转3
  3. 对全部i属于S,如果存在边j->i,那么置d(i)=min{d(i), d(j)+Wj->i},转2

 

复杂度分析

  Dijkstra 算法的时间复杂度为O(n^2)
  空间复杂度取决于存储方式,邻接矩阵为O(n^2)

算法实现
输入输出格式

  输入格式:
  第1行:一个数n,代表有n个节点
  第2-n+1行:每行n个数,代表图的邻接矩阵,没有边相连为-1
  输出格式:
  第1行:n-1个数,分别是1号节点到2-n号节点的最短路径

输入文件dijkstra.in

7
00 20 50 30 00 00 00
20 00 25 00 00 70 00
50 25 00 40 25 50 00
30 00 40 00 55 00 00
00 00 25 55 00 10 00
00 70 50 00 10 00 00
00 00 00 00 00 00 00

程序文件dijkstra.pas

program dijkstra;
var
state:array[1..100]of boolean;
data:array[1..100,1..100]of longint;
n,i,j,k,min,node:longint;
begin
	assign(input,'dijkstra.in');
	assign(output,'dijkstra.out');
	reset(input);
	rewrite(output);
	fillchar(data, sizeof(data), 0);
	fillchar(state,sizeof(state),0);
	readln(n);
	for i:=1 to n do
	for j:=1 to n do
	begin
		read(data[i,j]);
		if data[i,j]=0 then data[i,j]:=maxint;
	end;
	state[1]:=true;
	for k:=2 to n do
	begin
		min:=maxint;
		{查找权值最小的点为node}
		node:=1;
		for i:=2 to n do
		if (data[1,i]<min)and(state[i]=false) then
		begin
		min:=data[1,i];
		node:=i;
		end;
		{更新其他各点的权值}
		state[node]:=true;
		for j:=1 to n do
		if (data[1,node]+data[node,j]<data[1,j]) and (state[j]=false) then
		data[1,j]:=data[1,node]+data[node,j];
	end;
	for i:=1 to n-1 do
	if data[1,i]<>maxint then
		write(data[1,i],' ')
	else
		write(-1,' ');
	writeln(data[1,n]);
	close(input);
	close(output);

end.

编译运行后会产生输出文件dijkstra.out

$ fpc dijkstra.pas 
Free Pascal Compiler version 3.0.4 [2017/10/03] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling dijkstra.pas
Linking dijkstra
/usr/bin/ld: 警告: link.res 含有输出节;您忘记了 -T?
47 lines compiled, 0.1 sec
$ ./dijkstra 
$ cat dijkstra.out 
-1 20 45 30 70 80 32767

c++实现代码:

#include<fstream>
#include<cstring>
using namespace std;

const int MaxNum=1000000; //边权最大值
int n; //节点数目
int dist[501]; //到节点1的最短路径值
bool state[501]; //节点被搜索过状态指示
int data[501][501]; //邻接矩阵
//查找权值最小的节点
int findmin()
{
	int minnode=0, min=MaxNum;
	for(int i=1; i<=n; i++)
	  if ((dist[i]<min) && (!state[i]))
	  {
		  min=dist[i];
		  minnode=i;
	  }
	return minnode;
}

int main()
{
	ifstream in("dijkstra.in");
	ofstream out("dijkstra.out");
	memset(state, 0, sizeof(state));
	in >> n;
	for(int p=1; p<=n; p++)
	  for(int q=1; q<=n; q++)
	  {
		  in >> data[p][q];
		  if (data[p][q]==0) data[p][q]=MaxNum;
	  }
	//初始化
	for(int i=1; i<=n; i++)
	  dist[i]=data[1][i];
	state[1]=true;
	int done=1;
	while (done<n)
	{
		int node=findmin();
		if (node!=0)
		{
			done++; //找到的点的数目加1
			state[node]=true; //标记已经找到了从节点1到节点node的最短路径
			for(int i=1; i<=n; i++)//更新还没有找到的点的路径值
			  if ((dist[i]>dist[node]+data[node][i]) && (!state[i]))
				dist[i]=dist[node]+data[node][i];
		}
		else break;
	}
	for(int p=1; p<=n; p++)
	{
		if (dist[p]==MaxNum)
		  out<<-1;
		else
		  out<<dist[p];
		if (p==n)
		  out<<endl;
		else
		  out<<" ";
	}
	in.close();
	out.close();
	return 0;

}

dijkstra.in文件与前面一样的。

C++实现另一版本Dijkstra.cpp:

/*
 *
 * 问题 F: 求最短距离及其花费
 * 题目描述
 * 由n个点和m条无向边构成的无向连通图,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,
 * 如果最短距离有多条路线,则输出花费最少的。
 * 请用Dijkstra算法或Floyd算法等求解!
 * 输入
 * 输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t; 起点s,终点t。
 * (2<=n<=2000, n-1<=m<=(n*(n-1))/2, 1<=d<=10000, 1<=p<=10000, s != t)
 * 输出
 * 输出:一行有两个数, 最短距离及其花费。
 * 样例输入
 * 3 2
 * 1 2 5 6
 * 2 3 4 5
 * 1 3
 * 样例输出
 * 9 11
 *
 *
 *
 * */

#include<iostream>
using namespace std;
#define MAX 9999999

int map[2005][2005],pay[2005][2005];
int used[2005],dist[2005],pas[2005];
int p,q,n;
int i,j;
void dijkstra()
{
    for(i=1;i<=n;i++)
    {
        dist[i]=map[p][i];
        pas[i]=pay[p][i];
        used[i]=0;
    }
    dist[p]=0;
    used[p]=1;
    int num=1;
    while(num<n)
    {
        num++;
        int pos;
        int min=MAX;
        for(i=1;i<=n;i++)
        {
            if(!used[i]&&dist[i]<min)
            {
                min=dist[i];
                pos=i;
            }
        }
        if(min==MAX)
            break;
        used[pos]=1;
        for(i=1;i<=n;i++)
        {
            if(!used[i])
            {
                if(dist[i]>dist[pos]+map[pos][i])
                {
                    dist[i]=dist[pos]+map[pos][i];
                    pas[i]=pas[pos]+pay[pos][i];
                }
                else if(dist[i]==dist[pos]+map[pos][i]&&pas[i]>pas[pos]+pay[pos][i])
                    pas[i]=pas[pos]+pay[pos][i];
            }
        }
    }
    cout<<dist[q]<<" "<<pas[q]<<endl;
}
int  main()
{
    int a,b,c,d,m;
    while(cin>>n>>m&&n&&m)
    {
        for(i=0;i<=j;i++)
            for(j=0;j<=n;j++)
            {
                map[i][j]=MAX;
                pay[i][j]=MAX;
            }
           for(i=0;i<m;i++)
            {
                cin>>a>>b>>c>>d;
                if(map[a][b]>c)
                {
                 map[a][b]=map[b][a]=c;
                 pay[a][b]=pay[b][a]=d;
                }
            }
            cin>>p>>q;
            dijkstra();
    }
    return 0;
}

编译运行:

$ g++ -o Dijkstra Dijkstra.cpp 
$ ./Dijkstra 
3 2
1 2 5 6
2 3 4 5
1 3
9 11

参考:

最小生成树之克鲁斯卡尔(Kruskal)算法

转载于:https://my.oschina.net/u/2245781/blog/1603487

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值