【问题描述】
在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。
在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。
在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出源点至每一个其它顶点的最短路径长度。
【输入形式】
输入的第一行包含2个正整数n和s,表示图中共有n个顶点,且源点为s。其中n不超过50,s小于n。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
【输出形式】
只有一行,共有n-1个整数,表示源点至其它每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。
请注意行尾输出换行。
【样例输入】
4 1
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
【样例输出】
6 4 7
【样例说明】
在本题中,需要按照题目描述中的算法完成迪杰斯特拉算法,并在计算最短路径的过程中将每个顶点是否可达记录下来,直到求出每个可达顶点的最短路径之后,算法才能够结束。
迪杰斯特拉算法的特点是按照路径长度递增的顺序,依次添加下一条长度最短的边,从而不断构造出相应顶点的最短路径。
另外需要注意的是,在本题中为了更方便的表示顶点间的不可达状态,可以使用一个十分大的值作为标记。
算法讲解:
先说下什么是最短路径(从某个源点到其余个定点的最短路径)即从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径。
Dijkstra算法的基本思想
将图中的顶点分为两组:
U —已求出的最短路径的终点集合(初始为{v0})。
V-U —尚未求出最短路径的顶点集合(初始为V-{v0})。
按最短路径长度的递增顺序逐个将第二组的顶点加入到第一组中。
伪代码:
- 从v0出发到图上其余各顶点vi最短路径设为dist[i], 其初值为:dist[i]=
arcs[v0][vi].adj
,即v0至其他节点的边值 - 选择vj,使得 dist[j]=min{ dist[i] | vi∈V-U },即从V-U中找一顶点vj,使得dist[j]是v0到V-U中各顶点之间路径最短的。令U=U∪{ vj }
- 修改从v0出发到集合V-U上任一顶点vk可达的最短路径dist[k]。
- 重复(2)(3)共n-1次
步骤1:从v0出发到图上其余各顶点vi最短路径设为dist[i], 其初值为:dist[i]=arcs[v][vi].adj
初始时状态如下:
数组下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
node | A | B | C | D | E | F |
dist | ∞ | 10 | ∞ | ∞ | 100 | 60 |
path | # | AB | # | # | AE | AF |
In_U | Y | N | N | N | N | N |
node:节点 dist:路径长度 path:路径 In_U:是否在U集中
即初始时A只能从U集(目前只有A)到达其他节点的路径path和距离dist(如A到达E的距离为100,不能到达C)
步骤2:选择vj,使得 dist[j]=min{ dist[i] | vi∈V-U },即从V-U中找一顶点vj,使得dist[j]是v0到V-U中各顶点之间路径最短的。令U=U∪{ vj }(即,从不在U集中的元素中选出由A(起始点)可到达的最短路径,将其加入到U集中)
因此遍历dist中值,我们发现最小值为B,因此将B加入至U集,修改B(In_U为Y)
步骤3:修改从v0出发到集合V-U上任一顶点vk可达的最短路径dist[k]。
现在U集中由{A、B},因此可以修改A(或A间接经过B)至其他节点路径长度和路径轨迹
数组下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
node | A | B | C | D | E | F |
dist | ∞ | 10 | 40 | 30 | 100 | 60 |
path | # | AB | ABC | ABD | AE | AF |
In_U | Y | Y | N | N | N | N |
即A可由B到达C和D,且修改相应path
然后依次再执行步骤2和步骤3(循环n-1次)
数组下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
node | A | B | C | D | E | F |
dist | ∞ | 10 | 40 | 30 | 60 | 50 |
path | # | AB | ABC | ABD | ABDE | ABDF |
In_U | Y | Y | Y | Y | Y | Y |
PS:本例子数值取的可能不是很好,大家可以下去自己画一个图按照步骤进行理解
下面为小主奉上代码_:
map.h
#ifndef _MAP_H
#define _MAP_H
#include<stdio.h>
#include<limits.h>
#include<stdbool.h>
#define MAX_VER_NUM 1000 //最大节点数
#define MAX_DIS INT_MAX //用INT_MAX作为无穷值
typedef int VertexType; //节点数据类型
typedef int EdgeType; //边值数据类型
//定义图邻接矩阵
typedef struct Map{
VertexType Vertex[MAX_VER_NUM];
EdgeType Edge[MAX_VER_NUM][MAX_VER_NUM];
int vertexnum;
int edgenum;
}Map;
//定义U集(链表)访问节点后将其加入U集
typedef struct Node{
int Verdata;
struct Node *next;
}ListNode,* LinkList;
LinkList Path[MAX_VER_NUM];
int Dist[MAX_VER_NUM];
//map
void Init_DMap(Map *M);
void Creat_DMap(Map *M,int *v0);
void Find_MinPath(Map *M,int v0);
void print_minpath(LinkList L);
void Print_MinPaths(Map *M,VertexType v0);
void Print_Dist(int dist[],int v0,int vex_num);
//linklist
void Init_List(LinkList *L);
void AddTail(LinkList *L,VertexType ver);
bool Member(VertexType ver,LinkList s);
void CopyPath(LinkList *T,LinkList *S);
void Clear_Link(LinkList *L);
#endif // _MAP_H
map.c
#include"map.h"
#include<stdlib.h>
#include<stdbool.h>
void Init_DMap(Map *M)//邻接矩阵初始化
{
int i,j;
for(i=0;i<M->vertexnum;i++)
{
M->Vertex[i] = i;
}
for(i=0;i<M->vertexnum;i++)
{
for(j=0;j<M->vertexnum;j++)
{
M->Edge[i][j] = MAX_DIS;
}
}
}
void Creat_DMap(Map *M,int *v0)//初始化图
{
int i,j,Weight,s;
scanf("%d",&(M->vertexnum));//输入节点数
scanf("%d",&s); //输入源节点
*v0 = s;
Init_DMap(M);//初始化邻接矩阵
for(i=0;i<M->vertexnum;i++)
{
for(j=0;j<M->vertexnum;j++)
{
scanf("%d",&Weight);//输入有向边始终和权重
if(Weight != 0)
{
M->Edge[i][j] = Weight;
}
}
}
}
void Find_MinPath(Map *M,int v0)//寻找V0至其他节点最小路径
{
int i,k,t,min;
LinkList s;//初始化S集:最小路径集
for(i=0;i<M->vertexnum;i++)
{
Init_List(&Path[i]);//初始化最初路径
Dist[i] = M->Edge[v0][i];
if(Dist[i] < MAX_DIS)//如果有路径,加入
{
AddTail(&Path[i],M->Vertex[v0]);
AddTail(&Path[i],M->Vertex[i]);
}
}
Init_List(&s);//初始化S集
AddTail(&s,M->Vertex[v0]);//将起始点加入S集
for(t=1;t<M->vertexnum;t++)//循环M->vertexnum-1次
{
min = MAX_DIS;
for(i=0;i<M->vertexnum;i++)
{
if(!Member(M->Vertex[i],s) && Dist[i]< min)//节点i不在S集并且起始点到i的路径长度小于U-S集中最小值
{
k = i;
min = Dist[i];
}
}
AddTail(&s,M->Vertex[k]);
for(i=0;i<M->vertexnum;i++)
{
if(!Member(M->Vertex[i],s) && (Dist[k] + M->Edge[k][i] < Dist[i]) && Dist[k] + M->Edge[k][i]>0)
{
Dist[i] = Dist[k] + M->Edge[k][i];
CopyPath(&Path[i],&Path[k]);
AddTail(&Path[i],M->Vertex[i]);
}
}
}
}
void Print_Dist(int dist[],int v0,int vex_num)//打印距离
{
int i;
for(i=0;i<vex_num;i++)
{
if(i != v0)
{
if(Dist[i] == MAX_DIS)
{
printf("%d ",-1);
}else
{
printf("%d ",Dist[i]);
}
}
}
}
void print_minpath(LinkList L)//打印最小路径
{
ListNode *temp;
temp = L->next;
while(temp!=NULL)
{
printf("%d ",temp->Verdata);
temp = temp->next;
}
printf("\n");
}
void Print_MinPaths(Map *M,VertexType v0)
{
int i;
for(i=0;i<M->vertexnum;i++)
{
if(i != v0)
{
print_minpath(Path[i]);
}
}
}
void Init_List(LinkList *L)//初始化链表
{
(*L) = (LinkList)malloc(sizeof(ListNode));//创建头节点
(*L)->next = NULL;
}
void AddTail(LinkList *L,VertexType ver)//尾插法加结点
{
ListNode * temp, *tail;
tail = (*L);
while(tail->next != NULL)//找到尾节点
{
tail = tail->next;
}
temp =(LinkList)malloc(sizeof(ListNode));//申请一个节点
if(temp)
{
temp->Verdata = ver;
temp->next = NULL;//---> temp->next = NULL;
tail->next = temp;
}
}
bool Member(VertexType ver,LinkList s)
{
ListNode *temp;
temp = s->next;
while(temp != NULL)
{
if(temp->Verdata == ver)
return true;
temp = temp->next;
}
return false;
}
void CopyPath(LinkList *T,LinkList *S)// T = S;
{
ListNode *temp_s,*temp_t,*add;
temp_t = (*T);
temp_s = (*S)->next;
Clear_Link(T);
while(temp_s!=NULL)
{
add = (LinkList)malloc(sizeof(ListNode));
if(add)
{
add->Verdata = temp_s->Verdata;
add->next = temp_t->next;
temp_t->next = add;
temp_s = temp_s->next;
temp_t = temp_t->next;
}
}
}
void Clear_Link(LinkList *L)//清空链表
{
ListNode *temp,*del;
temp = (*L);
while(temp->next != NULL)
{
del = temp->next;
temp->next = del->next;
free(del);
}
}
main.c
#include<stdio.h>
#include"map.h"
int main()
{
int v0;
Map M;
Creat_DMap(&M,&v0);
Find_MinPath(&M,v0);
//Print_MinPaths(&M,v0);
Print_Dist(Dist,v0,M.vertexnum);
getchar();
return 0;
}