一、问题描述
1.问题描述
校园环境中存在 19 个核心功能区域(如校门、教学楼、图书馆、食堂等),师生及访客常面临三类核心问题:一是空间认知不足,新生和访客难以快速了解各区域的位置与功能;二是路径规划需求多样,包括两点两点间最短路径、全路径遍历及含自定义途经点的路径规划;三是输入交互不友好,用户易出现输入错误(如错别字、无效编号)导致操作中断。
本项目旨在构建一个基于图论算法的校园导览系统,通过建立校园空间拓扑模型,实现景点信息查询、多模式路径规划及智能输入处理等功能,解决上述校园导航痛点。
2.基本要求
(1)实现目标:
构建校园地图模型,实现 19 个核心区域的可视化展示。
支持景点信息查询,提供双模式(编号 / 名称)输入方式。
实现多种路径规划算法(Dijkstra、Floyd、SPFA、DFS、哈密顿路径)。
提供智能输入纠错功能,处理常见输入错误。
支持算法性能对比,验证不同算法的效率差异。
(2)数据输入:
算法功能选择:支持手动选择路径规划算法或其他功能(Dijkstra、Floyd 等)
景点查询:支持数字编号(1-19)或文字名称(如 "图书馆")两张模式输入
路径规划:起点和终点(支持编号 / 名称)、可选途经点(空格分隔,同样支持两种模式输入)
(3)数据输出:
校园地图:ASCII 格式的校园布局图,标注各区域相对位置。(如下图)
1.2.1中国农业大学东校区ASCII 格式的校园布局图
景点信息:包含功能定位、设施配置和使用场景的简要介绍。
最短路径:基于所选择的指定算法(如Floyd最短路径算法),显示指定两个景点的最短路径经过的具体节点及路径总长。
全路径结果:基于DFS算法,获取路径节点序列(如 "南门→公主楼")和总距离。
特殊路径:基于哈密顿通路算法进行改进,实现对于指定起点与终点时,用户输入特定的任意数量中间途经点,可以求出经过对应起点,中间途经点和终点的最短路径(允许存在回路)。
算法对比:不同算法的对于同一数据的实际执行时间和路径结果对比表。
3.初步思路
采用两种方式建模校园空间,其一为用节点表示景点,边表示路径及距离的邻接表,其二为二维邻接矩阵,当两点存在直接连接时,定义该元素为该道路的的长度,否则为无穷大。
设计双模式输入解析机制,通过对输入字符串的判断与解析实现名称与编号的快速映射。
加入输入纠错模块,通过字符串蛮力匹配推测可能的搜索目标,实现对异常输入的处理。
实现多种路径规划算法,针对不同场景提供最优解。
构建算法性能测试框架,记录不同最短路径算法的实际使用时间,量化对比不同算法的时间效率。
二、算法设计
1.原始模型
校园地图被抽象为一个无向带权图 G=(V,E),其中:
V 是顶点集合,包含 19 个顶点,每个顶点代表一个校园景点
E 是边集合,每条边代表两个景点间的可达路径
边权重表示对应路径的距离(单位:米)
具体表示如下:
2.1.1中国农业大学东校区校园地图抽象无权图
采用两种数据结构存储图:
邻接矩阵:vector<vector<int>> G,适用于 Dijkstra 和 Floyd 算法
2.1.2中国农业大学东校区校园地图二维邻接矩阵
邻接表:vector<vector<pair<int, int>>> adj,适用于 SPFA 算法
2.1.3中国农业大学东校区校园地图邻接表
景点信息存储在scenery结构体数组中,包含编号、名称和详细介绍,通过哈希表unordered_map<string, int>实现名称到编号的快速映射。
2.1.4景点映射示例
2.扩展优化模型
(1)路径规划算法优化
Dijkstra 算法:使用优先队列优化最短节点选择,将时间复杂度从 O (n²) 降至 O (m log n)。
哈密顿路径:采用状态压缩动态规划,通过dp[mask][last]表示访问状态,将时间复杂度控制在 O (m²・2ᵐ)(m 为途经点数量),并结合其他已有的最短路径算法改进,实现允许回路的出现。
Floyd算法:定义vector<vector<int> > P,添加二维动态数组P(path),用于记录每个节点在各自最短路径中的前驱节点,便于查找最短路径经过的具体节点。
(2)输入处理优化
双模式输入统一:接收用户输入的字符串后,对用户输入的字符串进行纯数字判定,若结果为true,则将字符串转为整型数字并送入其他算法输入端;若为false,则根据2.1.4图找到对应映射的景点编号,再将该编号送入其他算法输入端。
推测查询优化:当输入的字符串无法在双模式查询中得到对应的整型景点编号数字时,对该字符串进行关于所有景点名称的蛮力模式匹配,并将所有可发生模式匹配的景点作为用户可能希望查询的结果输出。
3.特殊处理
无效输入处理:对超出范围的编号或无法识别的名称,向用户提出输入错误,并返回输入部分,再次准备接收用户的输入。
比较算法性能功能:对于原本的所有最短路径算法进行的优化调整,得到的基于与那算的compare函数(如Floyd算法函数在性能比较时,调用的是FloydCompare函数),添加记录运行时间的功能的同时,删除了输入功能和路径记录的算法部分,仅保留接受输入和最短路径计算的核心算法部分,保证实际运行的代码与理论运行算法一致。
4.算法复杂度分析
(1)核心最短路径算法分析
Dijkstra 算法:时间复杂度在基础实现中为 O (n²),主要源于 “寻找最短距离节点” 和 “松弛操作” 两个 O (n) 步骤的嵌套循环。通过优先队列优化后,时间复杂度降至 O (m log n)(m 为边数),适用于节点数较多的稀疏图。在校园导览场景中(n=19,m≈40),优化后的 Dijkstra 平均耗时仅 3.2μs,是单点路径查询的最优选择。空间复杂度为 O (n),需存储距离数组、访问标记和前驱路径信息,内存占用低且稳定。
Floyd 算法:三重循环结构使其时间复杂度固定为 O (n³)。对于 n=19 的校园图,需执行 6859 次循环,平均耗时 11.5μs,可一次计算即可获得所有节点对的最短路径,适合系统初始化时的全量路径预计算(后期扩展方向)。空间复杂度为 O (n²),需存储 n×n 的距离矩阵和前驱矩阵,在节点数较少时(n<50)内存开销可控。
SPFA 算法:作为 Bellman-Ford 算法的队列优化版本,其时间复杂度呈现不确定性 —— 平均情况下为 O (m),接近线性效率;但在最坏情况下会退化为 O (nm)。在校园图(无负权边)中,SPFA 平均耗时 4.8μs,略高于 Dijkstra,空间复杂度为 O (n+m),主要用于存储邻接表和队列,适合边数较少的图模型。
DFS 全路径遍历:采用回溯法枚举所有可达路径,时间复杂度为 O ((n-2)!),呈现阶乘级增长。在 n=8 时路径数已超过 40000 条,平均耗时 78.6μs;当 n>10 时,性能急剧下降,该算法仅适合节点数极少的场景(如局部区域内的路径探索)。空间复杂度为 O (n),主要用于递归栈和路径存储,受限于递归深度。
以下是最短路径算法理论性能分析结果表示。
算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
Dijkstra | O(m log n) | O(n) | 单源最短路径 |
Floyd | O(n³) | O(n²) | 多源最短路径 |
SPFA | O(m)~O(nm) | O(n+m) | 含负权边的单源路径 |
DFS 全路径 | O((n-2)!) | O(n) | 全路径枚举 |
2.4.1最短路径算法理论性能分析
以下是实际运行结果的具体时间及其统计结果:
2.4.2最短路径算法实际时间性能结果(以南门为起点,以北门为终点)
(2)核心最短路径算法分析
哈密顿路径算法:针对含途经点的路径规划,采用状态压缩动态规划,时间复杂度为
O (m²・2ᵐ)(m 为途经点数量)。当 m=3 时仅需 800 次循环;但 m=10 时,2¹⁰=1024 种状态导致计算量激增。空间复杂度为 O (m・2ᵐ),用于存储 DP 状态矩阵,通过限制途经点数量(建议 m≤8)可有效控制内存开销。
输入推测算法:基于字符串匹配的输入推测机制,时间复杂度为 O (n・L)(n 为景点数,L 为输入字符串长度),空间复杂度为O(1)。在 n=19、L≤10 的场景下,对整体性能影响可忽略,却能将有效改善输入后的结果对于用户使用的影响,显著改善用户体验,并防止异常输入导致的代码错误运行。
算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
哈密顿路径 | O(m²·2ᵐ) | O(m·2ᵐ) | 带途经点的路径规划 |
输入推测 | O(n·L) | O(n) | 字符串匹配(L 为输入长度) |
2.4.4其他算法理论时间性能结果
由于校园环境的特殊性,输入推测算法无论是否需要进行暴力匹配,运行的问题规模都极低,因此可以忽略输入推测算法在面对不同输入时间和空间复杂度的变化。
三、调试分析
1.关键代码段说明
(1)Dijkstra 算法实现
3.1.1 Dijkstra算法核心算法部分
调试要点:优先队列的使用、路径回溯的正确性。核心部分在于能够构造合适的贪心策略,实现对数组的更新。
(2)Floyd最短路径算法
3.1.2Floyd算法核心算法部分
调试要点:关键在于构建遍历“中间点,起点,终点”的三重循环结构,从而对原二位邻接矩阵进行更新,并构造新的二位动态数组P(path),在原邻接矩阵更新时同步更新,从而记录前驱节点,便于后续根据P数组找出最短路径。
(2)SPFA最短路径算法
3.1.3 SPFA算法核心部分
调试要点:队列操作,确保仅在节点距离松弛且不在队列时入队,避免重复入队,同时出队后完整遍历邻接边不遗漏松弛操作;距离初始化与松弛,起点距离设 0、其余为合理无穷大,严格依据dist[v] > dist[u] + val执行松弛,通过日志核对更新前后距离值;邻接表完整性,无向图需确认双向边均添加、边权重赋值无误;路径回溯,前驱数组需与距离更新同步,回溯时从终点反向追溯再反转,确保路径连续无断层。
(4)DFS全路径算法
3.1.4 DFS遍历算法部分
调试要点:路径回溯的正确性,确定每次路径回溯时不同的数据是否需要修改;在递归中间节点,若不存在继续递归的路径,则开始回溯,直到存在未访问的邻接节点。每次递归时,判断是否法满足终止条件,若结果为false,向spot数组(记录访问过的节点编号)置入新的节点,更新visited数组,继续深度遍历,并以此次访问的节点作为新的起点,同时更新路径总长,直到终止判别为true,开始return,并向allpath数组中置入新的路径。
(5)哈密顿通路算法
3.1.5哈密顿通路算法部分-其一
3.1.6哈密顿通路算法部分-其二
调试要点:验证关键点映射的准确性,确保起点、终点及途经点被正确映射为连续索引,避免因离散节点导致的状态掩码错误;其次检查 DP 状态初始化与转移逻辑,确认dp[mask][last]初始值设置合理,状态转移时新掩码计算与距离更新公式正确,尤其注意mask | (1 << j)等位运算的准确性;再者验证路径重建的完整性,通过前驱数组回溯关键点顺序时需确保覆盖所有途经点,路径拼接阶段要检查中间节点是否正确填充,避免关键点间出现路径断层。
(6)双模式输入统一函数
3.1.7双模式输入统一函数部分
调试要点:对于输入的字符串首先进行纯数字判定,将纯数字转为整型数字,从各位开始计算每个字符和‘0’的差值,并将原记录的十进制数向左移一位(乘10)后加该插值,直到该字符串不再含有字符,并对结果整数进行范围判定,若为true,将保留,后续送入功能函数输入端,否则返回-1;若为字符串(非纯整型非负整数),则在原映射表中找出对应编号后保留,后续送入功能函数输入端,否则返回-1;若返回值为-1,则后续会送入暴力匹配算法,推测可能的输入。
2.后续优化
引入 A * 算法优化路径搜索,使用启发式函数提高搜索效率。
实现图的可视化展示,通过图形界面直观呈现路径规划结果。
加入用户行为分析,根据不同的起点与终点与历史输入推荐合适的算法。
优化Floyd函数,对频繁使用该算法的用户,添加长效数组记录全节点最短路径,避免重复计算。
四、测试结果
1. 功能测试
测试项 | 测试用例 | 预期结果 | 实际结果 | 测试状态 |
景点查询 | 输入 "15" 或 "图书馆" | 显示图书馆简要信息 | 符合预期 | 通过 |
输入纠错 | 输入 "门" 或 "20" | 对于"门",推荐“西门,北门,南门”,对于"20",提示重新输入 | 符合预期 | 通过 |
Dijkstra 算法 | 南门→图书馆 | 找到最短路径,长度约 500 米 | 符合预期 | 通过 |
Floyd 算法 | 计算所有点对路径 | 一次性得到所有节点间最短距离 | 符合预期 | 通过 |
SPFA算法 | 西门→体育馆 | 列出最短路径 | 符合预期 | 通过 |
DFS全路径遍历 | 西门→体育馆 | 列出所有可能路径并标注最短 | 符合预期 | 通过 |
途经点规划 | 南门→图书馆→食堂→北门 | 生成覆盖所有途经点的最优路径 | 符合预期 | 通过 |
算法时间性能比较 | 南门→北门 | 记录所有算法消耗的时间 | 符合预期 | 通过 |
4.1.1算法测试
2. 性能测试
在相同硬件环境下(个人设备),对各算法进行 10 次测试取平均值:
算法 | 测试场景 | 平均耗时 (μs) | 理论复杂度 | 实测复杂度趋势 |
Dijkstra | 南门→图书馆 | 3.2 | O(m log n) | 随边数线性增长 |
Floyd | 全节点对路径 | 11.5 | O(n³) | 随节点数立方增长 |
SPFA | 南门→图书馆 | 4.8 | O(m)~O(nm) | 表现稳定 |
DFS 全路径 | 西门→体育馆 | 78.6 | O((n-2)!) | 随节点数急剧增长 |
4.2.1最短路径算法实际时间性能统计结果(基于个人设备)
算法 | 测试场景 | 平均耗时 (μs) | 实测复杂度趋势 |
哈密顿路径 | 3 个途经点 | 6.5 | 随途经点数指数增长 |
输入推测 | 无特殊要求 | < 2 | 可忽略 |
4.2.2其他算法理论时间性能结果(基于个人设备)
3. 测试结论
系统实现了所有预期功能,各算法运行结果正确,性能表现符合理论复杂度分析。智能输入推测
功能有效提高了系统的容错性和用户体验。对于节点数为 19 的校园地图,各算法均能在可接受
时间内完成计算,满足实际应用需求。