实验四:无向图最短路径搜索

1.实验目的

熟练掌握图的操作,掌握 Dijkstra 算法的原理。

2.实验要求

输入:通过命令行参数输入起点和终点的位置名称。

输出:   (1)命令行参数不正确输出ERROR_01;

(2)获取最短路径失败时输出ERROR_02;

(3)获取最短路径成功时输出路径长度。

实际地图

1 右图为校内知名建筑物示意平面图(其中“传送门”用于增加网络复杂度),以边表示建筑物间的路径,各条路径上方的数字表示路径长度。      

2 针对该图进行构建数据结构和算法,通过命令行参数输入任意两建筑物的名称,可查询建筑物间的最短路径长度,并输出最短路径。

代码质量要求:

1、优选C语言,禁止直接调用C++ STL库;

2、除循环变量外,其它变量命名使用有明确含义的单词或缩写,不建议使用拼音;

3、禁止出现魔鬼数字;                                        4、添加必要的程序注释;

5、统一代码格式,例如:{}和空行;                    6、变量初始化,不要依赖默认赋值;

7、入参检查,“外部输入输入不可靠”,指针判空(一级指针、二级指针……),循环变量上下限;

8、malloc与free配对;                                       9、尽量少用全局变量;

10、编译错误解决,从前往后处理,提示出错的行不一定是错误的根因;

3.实验原理

图的存储和头文件的引入:

为了使得代码可以被重复使用,即代码被二次利用时只需要更改图的模式识别代码,同时改变宏定义之中的最大次数,本次将图的文件和宏定义的部分单独的写成一个头文件,在书写主程序时对他进行引用即可。

#ifndef      _COMMON_H//和文件名一致,将.换成_;前面加_。

#define     _COMMON_H//避免重复声明,使用if no define,define......endif

#include<stdio.h>//添加程序所需要的所有头文件

#include<string.h>

#define Status int//添加宏定义部分

static int a = 0 ;//可以定义静态变量,在所有.c文件中使用。

extern int  test(char* p,int n)//声明所有子函数

#endif

迪杰特斯拉算法思想:

设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

具体步骤:

(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。

(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。

(3) 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。

简明实例:

 

4.实验结果

 

5.问题与思考

思考:

对于Git的使用和一些其他的东西了解的不够深刻,对于编译器编译过程的理解依然不够深刻,这些都需要后续的学习去加以提升。

#.h文件

#pragma once
#ifndef _HEAD_H_
#define _HEAD_H_
#define InAccessible   100000		//无法抵达时的总长度
#define Status int
#define OK 2
#define TRUE 1
#define change 1					//用于扩大1位,进行占位和存放
#define FALSE 0
#define PLen 0
#define none -1
#define ERROR -1					//用于判定是否出现错误
#define OVERFLOW -2					//用于判断是否分配成功
#define ERROR_01 -3					//入参不正确
#define ERROR_02 -4					//无法生成虽短路径	
#define MaxNum 20					//最大支持的存放的节点数目


//图的数据结构
typedef struct GRAPH
{
	int vexs[MaxNum];				//定义结点矩阵,用于存放所有的结点
	int arcs[MaxNum][MaxNum];		//定义邻接矩阵,用于之后的计算和分析
	int vexnum;						//总的节点数目
	int arcnum;						//总的边的数目
}Graph;								//图的结构体的名称


//所有的地点名称及其所对应的在图中顶点的脚标
extern const char* PlaceName[13] = { "北门","饮水思源","传送门3","传送门1","腾飞塔","图书馆","传送门2","传送门4","教学主楼","宪梓堂","西迁馆","活动中心","南门" };
									 //0        1          2         3         4        5        6         7         8         9         10       11       12

//对图片进行初始化
extern Status InitMap(Graph* map)
{
	if (!map) return ERROR;									//鲁棒性检测
	map->vexnum = 13;										//所有结点的个数
	map->arcnum = 18;										//所有边的条数
	//初始化所有的结点和边
	int i = 0, j = 0;				
	for (i = 0; i < map->vexnum; ++i) {
		map->vexs[i] = i;
		for (j = 0; j < map->vexnum; ++j)					//对角线上初始化为0
			if (i == j) map->arcs[i][j] = 0;
			else {
				map->arcs[i][j] = InAccessible;				//全部先初始化为不连通
			}
	}
	//初始化邻接矩阵
	map->arcs[0][2] = map->arcs[2][0] = 22;
	map->arcs[0][1] = map->arcs[1][0] = 18;
	map->arcs[1][3] = map->arcs[3][1] = 27;
	map->arcs[1][4] = map->arcs[4][1] = 19;
	map->arcs[4][2] = map->arcs[2][4] = 4;
	map->arcs[4][7] = map->arcs[7][4] = 32;
	map->arcs[3][5] = map->arcs[5][3] = 4;
	map->arcs[5][6] = map->arcs[6][5] = 4;
	map->arcs[4][5] = map->arcs[5][4] = 23;
	map->arcs[5][8] = map->arcs[8][5] = 15;
	map->arcs[8][11] = map->arcs[11][8] = 21;
	map->arcs[11][12] = map->arcs[12][11] = 30;
	map->arcs[7][9] = map->arcs[9][7] = 4;
	map->arcs[8][9] = map->arcs[9][8] = 8;
	map->arcs[6][12] = map->arcs[12][6] = 21;
	map->arcs[6][10] = map->arcs[10][6] = 43;
	map->arcs[10][12] = map->arcs[12][10] = 20;
	map->arcs[9][12] = map->arcs[12][9] = 14;
	return OK;
}
extern Graph;
#endif

#.cpp文件

/*寻找图片中的最小路径的问题,张文硕-2206110686-自动化03*/

#include<stdio.h>		//调用内部包
#include<malloc.h>		//调用内存分配文件包
#include<string.h>
#include "map.h"		//调用map文件,其中存放了图的基本信息和宏定义信息,方便之后对图片文件进行跟换,只需更改InitMap即可。

typedef int PathMatrix[MaxNum][MaxNum+change];		//定义路径矩阵
typedef struct {
	int weight[MaxNum];								//定义最短路径长度和
	Status Visited[MaxNum];							//定义列表标记是否被访问过
}ShortPathList;

//判断入参数量是否正确
//如果参数为3,		则认为没有错误
//如果参数不为3,	则认为有错误
Status IfError_01(int argc)
{
	if (argc == 3) return FALSE;
	else 
	{
		printf("ERROR_01");
		return TRUE;
	}
}

//寻找输入的地点在地点列表中的位置
//如果找到了,则返回下标
//如果找不到,则返回ERROR_02
int FindPlace(const char* PlaceName[], char* name,Graph* map)
{
	if (!PlaceName || !name|| !map) return ERROR;					//鲁棒性检测
	int i = 0;
	while (i < map->vexnum)											//进行寻址
	{
		if (strcmp(PlaceName[i], name) == 0) return i;
		i++;
	}
	printf("ERROR_02");												//当跳出循环还没返回答案时,认为出错了
	return ERROR_02;
}

//定义函数初始化最短路径列表
//所有visit状态更新为FALSE
//所有权重进行处理,除自己到自己之外全部为无穷大,方便之后修改
Status InitList(ShortPathList* List,Graph map,int start)
{
	if (!List) return ERROR;
	for (int i = 0; i < map.vexnum; i++)
	{
		List->Visited[i] = FALSE;
		List->weight[i] = InAccessible;
	}
	List->weight[start] = 0;
	return OK;
}
//功能:让path矩阵的每行第一列表示路径长度.
//每行第一列全部初始化为FALSE
Status InitPath(PathMatrix& path,Graph map,int start)
{
	if (!path) return ERROR;
	for (int i = 0; i < map.vexnum; i++)
	{
		for (int j = 0; j < map.vexnum + change; j++)
		{
			path[i][j] = none;						//将所有的长度初始化为-1
		}
	}
	path[start][0] = 1;
	path[start][1] = start;
	return OK;
}
//功能:更新路径,使得路径更小
//输入:使得路径更新为到起点距离更小的路径,更小路径结点横坐标为now,原先的路径横坐标结点为before
//输出:状态
Status UpdatePath(PathMatrix &path,int before,int now,Graph map)
{
	if (!path) return ERROR;
	for (int i = 0; i < map.vexnum; i++)
	{
		path[before][i] = path[now][i];				//将小的直接抄过来
	}
	return OK;
}

//功能:修改结点到起点的路径长度最小值,并调用函数UpdatePath更新每个结点的最短路径。
//结果:如果新的长度小于原有长度,则进行更新。跟新后最后一个结点路径指向被更新的结点。
//                                           总的长度进行加一运算,同时对权重也进行更新。
Status ChangeWeight(Graph map, ShortPathList* list, int curnode, PathMatrix& path)
{
	if (!list||!path) return ERROR;
	for (int i = 0; i < map.vexnum; i++)
	{
		if (list->Visited[i] == FALSE)
		{
			if (list->weight[i] > list->weight[curnode] + map.arcs[curnode][i])
			{
				UpdatePath(path, i, curnode,map);										//当满足条件时更新路径
				path[i][PLen]++;															//总的路径长度path[i][0]加一
				path[i][path[i][PLen]] = i;												//最后一步补上当前的位置
				list->weight[i] = list->weight[curnode] + map.arcs[curnode][i];			//将权重更新为较小的值
			}
		}
	}
	return OK;
}
//功能:寻找最短的路径的结点并修改访问状态
//输入:list存放有每个节点到起点的距离;map用于存放图的信息
//输出:最小的所处的位置;
int FindShort(ShortPathList* list,Graph map)
{
	if (!list) return ERROR;
	int shortest=none;
	int flag = TRUE;
	int i = 0;
	while ( i < map.vexnum && flag)								//寻找第一个未被访问的结点
	{
		if (list->Visited[i] == FALSE)
		{
			shortest = i;
			flag =FALSE;
		}
		i++;
	}
	for (int i = 0; i < map.vexnum; i++)						//寻找所有节点中未被访问的最小长度结点
	{
		if (list->Visited[i] == FALSE)
		{
			if (list->weight[i] < list->weight[shortest])
			{
				shortest = i;
			}
		}
	}
	//if (shortest != none) list->Visited[shortest] = TRUE;		//更改访问状态
	return shortest;											//返回角标,如果角标是none,认为所有都访问完了,可以给结果了
}
//函数功能:求出指定起点到终点的最短路径,并且存放最短路径
/*输入:map		已经初始化完成的有向图
		start	起点序号
		end		终点序号
		list	存放最短路径长度的数组
		path	用于记录脚步的
  返回值:返回从start到end的最短路径权值*/
int DJT_ShortPath(Graph map, PathMatrix& path, ShortPathList* list, int start, int end)
{
	if (!path || ! list) return ERROR;
	InitList(list,map,start);
	InitPath(path, map,start);
	int cursor;
	while (FindShort(list, map) != none)
	{
		cursor = FindShort(list, map);
		list->Visited[cursor] = TRUE;
		ChangeWeight(map, list, cursor, path);
	}
	return list->weight[end];
}

Status PrintPath(PathMatrix path, int end,const char** PlaceName)
{
	if (!PlaceName) return ERROR;
	int flag = TRUE;
	for (int i = 0; i < path[end][0]; i++)
	{
		if (flag)
		{
			printf("\n%s", PlaceName[path[end][i + change]]);
			flag = FALSE;
		}
		else
		{
			printf("->%s", PlaceName[path[end][i + change]]);
		}
	}
	return OK;
}
int main(int argc, char* argv[])
{
	if(IfError_01(argc)) return ERROR_01;
	Graph* map = (Graph*)malloc(sizeof(Graph));
	if (!map) return OVERFLOW;
	InitMap(map);
	PathMatrix path = { none };														//定义记录路径的矩阵
	ShortPathList* list=(ShortPathList*) malloc(sizeof(ShortPathList));				//定义所有结点到起点的路径长度,
	if (!list) return OVERFLOW;								
	int start = FindPlace(PlaceName, argv[1], map);			//起始地点的坐标
	if (start == ERROR_02) return ERROR_02;
	int end = FindPlace(PlaceName, argv[2], map);			//终止地点的坐标
	if (end == ERROR_02) return ERROR_02;
	int shortestdistance = 0;
	shortestdistance=DJT_ShortPath(*map, path, list, start, end);					//寻找最小路径长度并将其存放进shortestdistance
	printf("%d", shortestdistance);
	//PrintPath(path,end,PlaceName);
	free(map);																		//释放map指针
	free(list);																		//释放list指针
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值