摘 要
最短路径不仅仅指一般地理意义上的距离最短,还可以引申到其他的度量,如时间、费用、线路容量等。相应地,最短路径问题就成为最快路径问题、最低费用问题等。由于最短路径问题在实际中常用于汽车导航系统以及各种应急系统等(110报警、119火警以及医疗救护系统),这些系统一般要求计算出到出事地点的最佳路线的时间一般在 18-3s,在行车过程中还需要实时计算出车辆前方的行驶路线,这就决定了最短路径问题的实现应该是高效率的[36]。最优路径问题不仅包括最短路径问题,还有可能涉及到最少时间问题、最少收费(存在收费公路)问题、或者是几个问题的综合,这时将必须考虑道路级别、道路流量、道路穿越代价(如红灯平均等待时间)等诸多因素。但是必须指出的是,一般来说最优路径在距离上应该是最短的,但最短路径在行驶时间和能源消耗的意义上未必是最优的[35]。其实,无论是距离最短、时间最快还是费用最低,它们的核心算法都是最短路径算法。
最短路径问题是图论理论的一个经典问题。寻找最短路径就是在指定网络中两结点间找一条距离最小的路。最短路不仅仅指一般地理意义上的距离最短,还可以引申到其它的度量,如时间、费用、线路容量等。
最短路径算法的选择与实现是通道路线设计的基础,最短路径算法是计算机科学与地理信息科学等领域的研究热点,很多网络相关问题均可纳入最短路径问题的范畴之中。经典的图论与不断发展完善的计算机数据结构及算法的有效结合使得新的最短路径算法不断涌现。本文主要解决的问题是:给定带权有向图G=(V,E),对任意顶点v,,v;EV(i≠j),求顶点v,到顶点v,的最短路径。即给定一个有向图,再给出任意2个不相邻的顶点,求2个顶点之间的最短距离。
关键词:最短路径 最优方案选取 最低成本 导航 带权有向图
- 绪 论
1.1 课设主要研究问题
最短路径问题是图论理论的一个经典问题。寻找最短路径就是在指定网络中两结点间找一条距离最小的路。最短路不仅仅指一般地理意义上的距离最短,还可以引申到其它的度量,如时间、费用、线路容量等。
最短路径算法的选择与实现是通道路线设计的基础,最短路径算法是计算机科学与地理信息科学等领域的研究热点,很多网络相关问题均可纳入最短路径问题的范畴之中。经典的图论与不断发展完善的计算机数据结构及算法的有效结合使得新的最短路径算法不断涌现。本文主要解决的问题是:给定带权有向图G=(V,E),对任意顶点v,,v;EV(i≠j),求顶点v,到顶点v,的最短路径。即给定一个有向图,再给出任意2个不相邻的顶点,求2个顶点之间的最短距离。
1.2 课设应用的理论知识
1.2.1 空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。算法空间复杂度的计算公式记作:S(n)=O(f{n)),其中,n为问题的规模,f{n)为语句关于n所占存储空间的函数。
1.2.2 图的基本概念
图:由顶点集V和顶点间的关系集合E(边的集合)组成的一种数据结构,可以用二元组定义为:G=(V,E)。
有向图:在图中,若用箭头标明了边是有方向性的,则称这样的图为有向图。
权:在图的边或弧中给出相关的数,称为权。 权可以代表一个顶点到另一个顶点的距离,耗费等,带权图一般称为网。
邻接矩阵:表示一个图的常用存储表示。它用两个数组分别存储数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息。
第二章 课设实现过程
2.1 穷举法
2.1.1穷举法描述
穷举搜索(Exhaustive Search'Algorithm)法又称列举法,其基本思想是逐一列举问题所涉及的所有情况。穷举法常用于解决“是否存在”或“有多少种可能”等问题。
穷举法的算法特点是算法简单,但是运行时所花费的时间量大,需要将问题
所涉及的有限种情形须一一列举,既不能重复,又不能遗漏。用穷举法实现广度优先搜索。广度优先搜索算法是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位址,彻底地搜索整张图,直到找到结果为止。
2.1.2穷举法设计
对问题使用广度优先遍历,将所有可能的结果首先保存起来,再在结果中查
找最短路径的结果,打印出来。
其算法流程如图 2.1所示,其算法步骤可以描述为如下:
(1)从文件中读取图的节点数目、读取节点数目Npoint、起始点Start、结
束点End、邻接矩阵**WeightArry。
- 动态分配存储空间MyMark[Npoint!]。
(3)初始化路径存储状态为可更新状态和第0个路径,MyMark[*].state=0
MyMark[*].path[0]=Start。
(4)依次更新MyMark的第1到(Npoint-1)路径。
1)计算每次更新存储空间数目 m =(Npoint-i-1)!:
2)依次更新j=0到(Npoint!/m)组存储路径的节点:
a.判断存储是否可以更新,不可以则返回到2),即判断MyMark[m*j]==0?:
b. 计算出第i-1个路径之后的下一个路径nextPathPoint,并得到2个点之间的距离;将路径更新到MyMark[m*j+ k]中;
d. 判断nextPathPoint是否已经是最终的点,更新该组存储空间状态为完成:
e.判断距离是否不可以到达,更新该组存储空间状态为不可到达。
- 在MyMark中查找出总代价最短的点,打印结果。
其中说明如下:
(1)对2个节点之间不可到达,编程时候用MAXSIZE=1000代替。
(2)读取文件方法已经封装为一个类CfileRead,其中可以返回文件是否异
常、节点数目、起始点、终点等信息。
(3)邻接矩阵**WeightArry为动态分配的二维指针,用来存放节点的权值。
(4)节点名称省略,统一用0、1、2……来标示。
(5)MyMark[Npoint!]为动态分配的存储遍历信息的结构体,类型为mark,结构体定义为:
typedef struct mark
{
int price;
int n;//路径数目,含头尾
int path[MAXPOINT+ 1]; /存储路径
int states;//0:正常:-1,此路不通;1此路已经结束
}mark;
Price:为这条路径的总代价;n:标示这条路径节点数目;path:依次存放的路径编号;states:标示这条路径的状态:1为完成了从初始点到终点、0为该路径还没有完成、-1为该路径已经不可到达。
(6)在路径更新中,起点均为iStart,然后循环往后依次增加不重复的路
径,不重复指的是不与本存储中的路径冲突。
实现函数: int getArryBigM(int arr[], int n, int N, int M);
函数功能:返回比原来arr数组中最后一个数,大于M的不重复数字。
返回值:-1即没有这个数;否则返回应该填入数据。
输入:arr 数组,n数组中个数,N最大值,M为加多少。
2.1.3穷举法分析
针对本最短路径问题,穷举法实现比较简单,但是时间复杂度和空间复杂度比较大,空间复杂度为0(n!),时间复杂度为o(n”)。
在这里可以对算法进行如下改进:设定一个变量MinPrice=MAXSIZE 存储当前代价最小值,首先判断v,到,是否可以直接到达,可以则将其距离更新为最小代价值 MinPrice,在以后的遍历中,路径的代价如果大于 MinPrice 则设置其状态为不可到达:若路径已经完成,且代价小于 MinPrice,则 MinPrice 用现有完成路径代价替换。
程序代码
#include <stdio.h>
#include <stdlib.h>
#define M 5000 //假设两顶点之间没有路径,用5000来表示
typedef struct vexsinfo //存放顶点信息的结构体
{
int park; //访问的标志,park=0是未被访问过
int num; //景点的编号
char name[32]; //景点的名称
char introduction[256]; //景点的介绍
}vexsinfo;
typedef struct MGraph
{
int r; //记录最短路径访问过的景点数目
int minrude; //记录最短路径的长度
int min[50]; //记录最短路径经过的顶点信息
int a[50]; //记录路线的数组
vexsinfo vexs[50]; //存放顶点编号的数组,用vexsinfo结构体的变量vexsinfo定义,可以用
//该数组存放顶点信息
int arc[50][50]; //存放两点之间权值的邻接矩阵
int v, e; //定点数和边数
} MGraph;
MGraph* CreateGraph()
{
MGraph* G;
int i, j, k;
G = (MGraph*)malloc(sizeof(MGraph));
//初始化访问标志
for (i = 0; i < 10; i++) {
G->vexs[i].park = 0;
}
//初始化顶点数目和路线数目
G->v = 10;
G->e = 13;
//给景点数组编号
for (i = 1; i <= G->v; i++)
G->vexs[i].num = i;
for (j = 1; j <= 10; j++)
for (k = 1; k <= 10; k++)
{
G->arc[j][k] = M;
}
//初始化矩阵,赋予每条边权值
G->arc[1][2] = G->arc[2][1] = 1;
G->arc[1][3] = G->arc[3][1] = 3;
G->arc[1][10] = G->arc[10][1] = 8;
G->arc[2][6] = G->arc[6][2] = 2;
G->arc[4][3] = G->arc[3][4] = 1;
G->arc[4][5] = G->arc[5][4] = 1;
G->arc[9][5] = G->arc[5][9] = 2;
G->arc[6][7] = G->arc[7][6] = 1;
G->arc[7][8] = G->arc[8][7] = 2;
G->arc[10][7] = G->arc[7][10] = 3;
G->arc[8][9] = G->arc[9][8] = 2;
G->arc[8][10] = G->arc[10][8] = 2;
G->arc[9][10] = G->arc[10][9] = 3;
//初始化顶点信息
strcpy(G->vexs[1].name, "烂尾楼");
strcpy(G->vexs[2].name, "主教学楼");
strcpy(G->vexs[3].name, "艺术楼");
strcpy(G->vexs[4].name, "信息楼");
strcpy(G->vexs[5].name, "体育馆");
strcpy(G->vexs[6].name, "操场");
strcpy(G->vexs[7].name, "食堂");
strcpy(G->vexs[8].name, "男寝");
strcpy(G->vexs[9].name, "女寝");
strcpy(G->vexs[10].name, "校门");
strcpy(G->vexs[1].introduction, "未知");
strcpy(G->vexs[2].introduction, "大部分学生上课以及老师办公室的地点");
strcpy(G->vexs[3].introduction, "主要给艺术专业同学使用");
strcpy(G->vexs[4].introduction, "主要给信息传播专业使用");
strcpy(G->vexs[5].introduction, "要钱");
strcpy(G->vexs[6].introduction, "2021年新建");
strcpy(G->vexs[7].introduction, "有三层");
strcpy(G->vexs[8].introduction, "有3栋");
strcpy(G->vexs[9].introduction, "有5栋");
strcpy(G->vexs[10].introduction, "北湖东区的校门");
return G;
}
void RudeGraph(MGraph* G, int b, int w, int k, int sum) {
int p, j, n=0;
if (b == w) {
for (p = 0; p < k; p++) {
printf("%d->", G->a[p]);
}
printf("%d ", G->a[k]);
printf("路线总长:%dkm\n", sum);
if (sum < G->minrude) {
G->r = k;
G->minrude = sum;
for (p = 0; p <= k; p++) {
G->min[p] = G->a[p];
}
}
return;
}
else {
for (j = 1; j <= 10; j++) {
if (G->arc[b][j] < 5000 && G->vexs[j].park == 0) {
k++;
G->a[k] = j;
sum += G->arc[b][j];
G->vexs[j].park = 1;
RudeGraph(G, j, w, k, sum);//通过递归对所有路径进行深度搜索
k--; //递归返回这一层后对顶点信息进行重新初始化
G->vexs[j].park = 0;
sum -= G->arc[b][j];
}
}
}
return;
}
int main(void)
{
int c, i, p, k=0;
MGraph* T;
T = CreateGraph();
while (1) {
printf("*************************\n");
printf("欢迎来到CCUT大学景点信息服务系统\n");
printf("1.景点信息查询\n");
printf("2.路线查询服务\n");
printf("3.退出\n");
printf("*************************\n");
printf("请选择你要查询的功能:\n");
scanf_s("%d", &c);
if (c == 1) {
printf("*************************\n");
printf("*大学共有如下十处景点:\n");
for (i = 1; i <= 10; i++) {
printf("%d.", T->vexs[i].num);
printf("%s: ", T->vexs[i].name);
printf("%s\n", T->vexs[i].introduction);
}
}
else if (c == 2) {
printf("*************************\n");
printf("请输入当前景点编号和你想要去的景点编号:\n");
printf("(注:景点编号可在功能1内查询)\n");
int b, w;
//初始化访问标志
for (i = 0; i < 10; i++) {
T->vexs[i].park = 0;
}
scanf_s("%d %d", &b, &w);
while (b < 1 || b>10 || w < 1 || w>10) {
printf("输入错误,请重新输入:\n");
scanf_s("%d %d", &b, &w);
}
if (b == w) {
printf("您已经在此景点,请重新输入:\n");
scanf_s("%d %d", &b, &w);
}
else {
T->a[0] = b;
T->vexs[b].park = 1;
printf("从景点%d到景点%d共有如下路径:\n", b, w);
RudeGraph(T, b, w, 0, 0);
printf("最短路径为:\n");
for (p = 0; p < T->r; p++) {
printf("%d->", T->min[p]);
}
printf("%d ", T->min[T->r]);
printf("路线总长:%dkm\n", T->minrude);
T->minrude = 100; //重新初始化最短路径长度
}
}
else if (c == 3) break;
else printf("输入错误,请重新输入:\n");
}
printf("祝您生活愉快,再见^v^");
return 0;
}