新闻详情

新闻详情

首页 / 资讯中心 / 详情

数据结构课程设计实战:基于Dijkstra的最短路径系统与答辩全攻略

发布时间:2026/9/25 4:12:54来源:尧图网络
数据结构课程设计实战:基于Dijkstra的最短路径系统与答辩全攻略
简介杭电数据结构课程设计已通过验收的完整实践资料包含停车场管理问题和校园导航咨询系统两个项目。资源面向正在完成数据结构课程设计的高校学生尤其适合需要参考完整实现与实验报告格式的读者两个项目分别涉及栈、队列、链表、图等核心结构并应用最短路径等算法体现理论与实践结合。压缩包共十一个文件大小约九百三十千字节涵盖源码文件、头文件、实验报告、地图数据、矩阵表格、示意图以及可执行程序覆盖代码、文档、数据与演示的完整链路。目前已有四百六十人学习读者可获得可运行的停车场管理模拟程序与校园导航系统、完整的课程设计报告以及各模块的代码组织与算法实现思路便于对照验收标准优化自己的作品。1. 这不是算法题是“带着代码去答辩”HDU 的数据结构课程设计跟平时刷杭电 OJ 那道坎完全是两码事。OJ 题错了就错了改到 AC 为止没人问你为什么这么写课程设计要过验收你得拿着一个能跑的 .exe当着老师的面演示功能然后回答“为什么用邻接表不用邻接矩阵”“这个排序在最坏情况下是什么复杂度”“输入一万条数据会不会崩”。很多同学代码写完了一进验收教室被问三句话就卡壳最后得了个合格但心里没底。这篇笔记就是按“能跑、能讲、能过验收”的标准来讲的方向是数据结构与算法落地里最常见的那套图的遍历与最短路径、二叉树操作、排序与查找以及一套完整的交互界面。标题里的“通过验收”才是真正的主菜。代码只是入场券验收看的是你的选题够不够粒度、需求分析有没有边界、测试数据能不能自圆其说、答辩时原理讲不讲得清。下面我会把我做这个课设的完整思路拆给你怎么选题目、怎么定数据结构、代码怎么从主函数往下长出来、以及验收前那个晚上到底该检查什么。新手能照着一路做到提交熟手可以直接跳到第 5 章看踩坑清单和答辩问法。2. 选题与需求分析决定你后面两个星期过得好不好2.1 常见选题的分类与能力匹配杭电数据结构课设的题目池每年都在换但翻来覆去就是那几类。我按“数据结构的核心程度”帮你分个类你看自己适合哪类。第一类是“图的算法型”比如校园导航、城市交通查询、迷宫求解。核心是图的存储与遍历Dijkstra 或者 Floyd 必考其一。这类题目数据结构感最强答辩时能讲的东西多但代码量也最大。第二类是“线性结构应用型”比如学生信息管理、图书借阅管理、员工工资查询。核心是结构体数组或链表配合多关键字排序和查找。这类题目好写但要过得漂亮得在“查找算法对比”和“数据量增大时的性能变化”上做文章不能只会个冒泡排序。第三类是“树型应用”比如哈夫曼编码/译码、表达式求值、二叉排序树。哈夫曼是经典中的经典因为编码过程能可视化出哈夫曼树答辩时有图可看表达式求值则是栈和二叉树两个知识点一起考了。我不建议选那种“系统管理”味太重的题比如“超市收银系统”“图书馆管理系统”。这些题目听着实用但其实数据结构浓度不够——你说你是用顺序表还是链表老师一问“那顺序表和链表各自的适用场景是什么”你只能说“都可以”这就很被动。我当时的选题是一个“基于图的最短路径查询系统”图的顶点是校园建筑边是路径距离。为什么选它因为图这个数据结构在课设里表现力最强邻接矩阵存起来直观Dijkstra 讲起来有条理还能顺手做一个 DFS 遍历把“可达性查询”也囊括进来一个题目覆盖多个考点验收时老师可问的点多你展示的空间也大。2.2 拿到题目的第一个小时把需求写成一页纸别急着开 IDE。你拿到题目后最该做的是把“人话需求”翻译成“数据结构语言”。我给你一个模板照着填就行。系统名称校园建筑最短路径查询系统输入源建筑编号、目的建筑编号输出最短路径长度、途经建筑序列额外功能显示所有建筑列表、查询某建筑可直达哪些建筑边界情况源和目的相同、图不连通、非法编号输入这一页纸就是你的需求说明书初稿。后面写中期报告、课程设计报告的时候直接把这份内容扩写就行不用再绞尽脑汁想“我做了什么”。然后做一件事画出数据的组织方式。用表格列出来建筑信息放哪、边信息放哪、路径输出时用到的辅助数组放哪。这一步能帮你发现很多问题——比如你想用 Floyd 而不是 Dijkstra那就要多一个二维路径矩阵如果想支持按距离排序输出全部路线那就要考虑怎么在多条路径里做比较。这些都是后面代码里绕不开的决策现在想清楚比写完了再推倒重来省力得多。2.3 验收视角的需求补充老师想在你的演示里看到什么前面那页纸是“功能需求”但课设验收还有一层“展示需求”。老师一天要看几十个组不会仔细读你的代码他主要通过几分钟的演示来判断你这个课设“做没做到位”。所以你的需求分析里至少要包含三个能“看得见”的点。第一初始化过程可视化——启动程序时打印出读入了多少个顶点、多少条边让老师确定你的数据是加载进来的不是写死在代码里的。第二一个足够复杂的测试用例——选一条明显不是最短的路径让程序算出来后老师能肉眼验证结果是对的。第三异常输入的优雅处理——故意输一个不存在的编号程序不能崩溃不能死循环要输出一句人能看懂的提示。这三条就是验收现场的高光时刻。很多同学代码没问题败就败在演示时输入了“5”结果编号只有 0 到 3程序直接数组越界崩了老师眉头一皱后面答辩你心里就慌了。这些都是你在需求分析阶段就该写进“边界情况”里的东西。3. 核心数据结构与算法选型把“要用什么”焊死在设计文档里3.1 邻接矩阵还是邻接表别只看教材怎么说图的最短路查询系统第一个决策就是图的存储结构。教材上会告诉你稀疏图用邻接表稠密图用邻接矩阵。这个结论没错但课设场景下你要多想一层。课设的数据量就那么大你录入 20 个建筑、40 条路邻接矩阵也就是一个 20×20 的二维数组400 个 int占 1600 字节。这个量级谈性能优化没有意义。此时你应该优先考虑“哪个写起来不容易错、答辩时更好讲”。我用的邻接矩阵。原因有三个第一Dijkstra 算法用邻接矩阵写代码形态跟教材伪代码一一对应你背得住也讲得清第二答辩时老师让你“把图结构画出来”邻接矩阵直接在纸上画个方格子填 0 和 ∞ 就行特别直观第三检查图的连通性、查一条边是否存在矩阵是 O(1) 的你演示“查询两栋建筑是否直接相连”这个功能时代码不用绕弯子。邻接表当然也可以。如果你选的题目是“微博关注关系分析”那必须用邻接表——因为十万个顶点、百万条边用矩阵就是灾难。选哪个不是看哪个高级而是看数据长什么样。我一般建议顶点数预期超过 100 或者边特别稀疏不到完全图的 10%考虑邻接表否则就邻接矩阵省心。3.2 Dijkstra 的课设版本记录路径而不只是距离教材上的 Dijkstra 通常只求最短路径长度用一个 dist[] 数组搞定。但课设演示需要输出完整路径所以你还得维护一个 prev[] 数组记录每个顶点的前驱顶点。这是课设版 Dijkstra 和算法题版 Dijkstra 最大的区别。#define MAXVEX 20 #define INF 65535 typedef struct { char name[20]; // 建筑名称 int id; // 建筑编号 } Vertex; typedef struct { int edges[MAXVEX][MAXVEX]; // 邻接矩阵INF 表示不连通 int numVertexes, numEdges; // 实际顶点数和边数 Vertex vexs[MAXVEX]; // 顶点数组 } MGraph; // Dijkstra 算法求 start 到所有点的最短路径 // dist: 输出最短路径长度 // prev: 输出前驱顶点数组用于回溯路径 void Dijkstra(MGraph *G, int start, int dist[], int prev[]) { int final[MAXVEX] {0}; // final[i]1 表示顶点 i 已确定最短路径 int i, j, k, min; for (i 0; i G-numVertexes; i) { dist[i] G-edges[start][i]; prev[i] (dist[i] INF) ? start : -1; } final[start] 1; dist[start] 0; for (i 1; i G-numVertexes; i) { min INF; k -1; for (j 0; j G-numVertexes; j) { if (!final[j] dist[j] min) { min dist[j]; k j; } } if (k -1) break; // 剩余顶点不可达提前结束 final[k] 1; for (j 0; j G-numVertexes; j) { if (!final[j] min G-edges[k][j] dist[j]) { dist[j] min G-edges[k][j]; prev[j] k; } } } }这段代码里最关键的设计是prev数组的初始化prev[i] (dist[i] INF) ? start : -1。这里把“直接与起点相连”和“不与起点相连”两种情况区分开不然后面回溯路径时会把不相连的顶点错误地串进来。k -1那个 break 也是实际测试逼出来的——如果图本身不连通没有这个判断内层循环会把k留在 -1下一轮final[-1]就是数组越界。你写代码时一定要加上这种防御性判断尤其是课设这种“演示时输入不可控”的场景。回溯路径的代码也要单独写一个函数不要在 Dijkstra 里顺手打印否则你的 Dijkstra 就不纯净了——它既要算距离又要做 IO答辩时不好讲清楚。void PrintPath(int start, int end, int prev[]) { // 用栈逆序输出前驱链避免递归深度不确定 int stack[MAXVEX], top 0; int cur end; while (cur ! -1) { stack[top] cur; if (cur start) break; cur prev[cur]; } // 此时栈顶是 start依次出栈即为路径 while (top 0) { printf(%s, vexs[stack[--top]].name); if (top 0) printf( - ); } printf(\n); }为什么用栈不用递归因为课设程序的路径长度最长可能等于顶点数递归层数虽然不多但老师可能会问“如果有一千个顶点这个递归会不会爆栈”。你用栈写一方面展示了“用数据结构解决问题”的意识另一方面彻底规避了递归深度的争议。答辩时这句话可以直接说我用栈来逆序前驱链是为了不依赖递归避免最坏情况下顶点数较大时的栈溢出风险。3.3 排序与查找第二考点不能瘸腿只做最短路课设撑不起来。老师会问你这个系统里还有没有别的数据结构知识点所以你得主动在系统里塞一个“排序”功能。我当时的做法是支持按建筑名称的字典序输出全部建筑列表以及按距离排序输出“从一个建筑出发的所有可达路径”。// 按距离从小到大排序所有从 start 出发的边 // 用结构体数组 qsort避免手写冒泡的尴尬 typedef struct { int adjvex; // 邻接顶点编号 int weight; // 边权距离 } EdgeInfo; int cmpEdge(const void *a, const void *b) { return ((EdgeInfo*)a)-weight - ((EdgeInfo*)b)-weight; } void SortEdgesByWeight(MGraph *G, int start) { EdgeInfo edges[MAXVEX]; int n 0; for (int i 0; i G-numVertexes; i) { if (G-edges[start][i] ! INF) { edges[n].adjvex i; edges[n].weight G-edges[start][i]; n; } } qsort(edges, n, sizeof(EdgeInfo), cmpEdge); printf(从 %s 出发的路径按距离排序\n, G-vexs[start].name); for (int i 0; i n; i) { printf( %s (%d 米)\n, G-vexs[edges[i].adjvex].name, edges[i].weight); } }这里刻意用了qsort而不是自己写快排是留了答辩余地的如果老师问你“你自己实现过排序吗”你就说“课设里我用了 C 标准库的 qsort 来处理数据排序因为它的比较器接口清晰、性能稳定我在平时练习时也手写过快速排序和归并排序能讲清楚分治思路。” 这比你在课设里贴一个自己写的、边界都处理不对的快排要安全得多。课设不是算法竞赛不要在核心业务里冒险。查找方面因为顶点数量少顺序查找就够。但答辩可能会问“为什么不用二分查找”你得能答上来顺序查找适用于无序列表而建筑名称列表如果允许增删就不保持有序性所以二分查找不适用如果想要 O(log n) 查找可以在初始化时维护一个按名称排序的索引数组。你看这个问题你一答老师就知道你是真懂查找的适用条件而不是背了个二分模板。4. 从主函数长出来的完整代码可复现的骨架与七个必经步骤4.1 先写 main 的骨架菜单循环是课设的脸面课程设计不是写一个被调用的库函数而是一个能交互的程序。你的 main 要是一个循环打印菜单、接收输入、分发任务的结构。这一步别写在最后第一步就写。int main() { MGraph G; int choice; int start, end; InitGraph(G); // 从文件读入数据初始化图 PrintWelcome(); // 打印系统名称和作者信息 while (1) { PrintMenu(); // 打印功能菜单 scanf(%d, choice); while (getchar() ! \n); // 清空输入缓冲防止残留换行 switch (choice) { case 1: ListAllBuildings(G); break; case 2: QueryShortestPath(G); break; case 3: SortEdgesByWeight(G); break; case 4: DFS_Traverse(G); break; case 0: printf(感谢使用再见\n); return 0; default: printf(输入错误请重新选择\n); break; } } return 0; }这段骨架里有几个细节是血泪经验。第一while (getchar() ! \n)这一行必须有不然你输入完数字按回车残留的换行会被下一个 %c 或 %s 吃掉然后你的菜单就乱跳了。第二case 0和default必须分开0 是正常退出其他数字是错误输入这两件事不能用同一个分支否则老师会觉得你程序的控制流不清晰。第三每个 case 末尾都要有 break这不用我说你也知道但漏 break 恰恰是课设代码里最常见的问题因为 CtrlC 复制上一段的时候很容易连着 break 一起删掉。菜单函数本身要打印清楚每个数字对应什么功能别用“1.xxx 2.xxx”这种紧凑格式要把边界情况也写上去比如“0. 退出”让使用者不困惑。4.2 数据的正确打开方式文件读取而不是交互录入用 scanf 一个顶点一个顶点地录入图数据是一种自虐行为——演示时你光输入数据就要两三分钟老师早就失去耐心了。正确做法是把数据放在文本文件里程序启动时一次性读取进来。#define MAX_NAME 20 void InitGraph(MGraph *G) { FILE *fp fopen(graph_data.txt, r); if (fp NULL) { printf(错误找不到 graph_data.txt 文件\n); printf(请确认数据文件与程序在同一目录下。\n); exit(1); } fscanf(fp, %d %d, G-numVertexes, G-numEdges); for (int i 0; i G-numVertexes; i) { fscanf(fp, %d %s, G-vexs[i].id, G-vexs[i].name); } // 初始化邻接矩阵 for (int i 0; i G-numVertexes; i) { for (int j 0; j G-numVertexes; j) { if (i j) G-edges[i][j] 0; else G-edges[i][j] INF; } } // 读取边 int v1, v2, weight; for (int i 0; i G-numEdges; i) { fscanf(fp, %d %d %d, v1, v2, weight); G-edges[v1][v2] weight; G-edges[v2][v1] weight; // 无向图 } fclose(fp); printf(初始化成功共 %d 个建筑%d 条道路。\n, G-numVertexes, G-numEdges); }数据文件 graph_data.txt 的格式是有讲究的按行读的别用逗号分隔fscanf 的空格分隔最不容易出错6 6 0 图书馆 1 第一教学楼 2 第二教学楼 3 学生食堂 4 体育馆 5 宿舍区 0 1 200 0 2 350 1 3 180 2 3 250 3 4 300 4 5 150这里有个坑顶点的 id 必须和数组下标完全一致。如果你从 1 开始编号那数组就要多开一位或者读入时id--。我的建议是数据文件里就从 0 开始编号和数组下标天然对齐省去所有转换逻辑。这看起来不优雅但课设不需要优雅需要的是少一个出错的可能。4.3 手动模拟一遍 Dijkstra把 prev 数组画出来给你看代码可以打印算法流程光看代码是不容易“懂”的。我建议你写完 Dijkstra 函数后拿上面这个 6 顶点的小图手动跑一遍把 dist 和 prev 的变化写在一张表上。以顶点 0图书馆为起点初始时dist [0, 200, 350, INF, INF, INF]prev [0, 0, 0, -1, -1, -1]。第一轮找到未确定点中 dist 最小的是顶点 1dist200。确定它。然后更新与 1 相邻的点顶点 3 的 dist 从 INF 变成 200180380prev[3]1。第二轮未确定点中 dist 最小的是顶点 2dist350。确定它。更新顶点 3380 与 350250600 比较380 更小所以 prev[3] 保持 1dist[3] 不动。第三轮顶点 3dist380确定。更新顶点 4dist680prev[4]3。第四轮顶点 4dist680确定。更新顶点 5dist830prev[5]4。第五轮顶点 5 确定结束。如果你要查从图书馆到宿舍区的路径从 prev[5]4prev[4]3prev[3]1prev[1]0反推得到路径0 - 1 - 3 - 4 - 5也就是图书馆 - 第一教学楼 - 学生食堂 - 体育馆 - 宿舍区总长度 200180300150 830 米。把这张表放在你的课程设计报告里比贴代码有用得多。老师看报告时看到你能把算法的运行过程画出来就证明你真的会 Dijkstra而不是从网上找了一段代码改改名。4.4 菜单功能与代码文件的划分没有第三人称的项目结构课设代码不建议写完丢在一个 main.c 里也不建议拆成一百个文件。推荐结构是graph.h 放结构体定义和函数声明graph.c 放图的初始化、Dijkstra、排序等核心实现main.c 放交互逻辑。// graph.h #ifndef GRAPH_H #define GRAPH_H #define MAXVEX 20 #define INF 65535 typedef struct { char name[20]; int id; } Vertex; typedef struct { int edges[MAXVEX][MAXVEX]; int numVertexes, numEdges; Vertex vexs[MAXVEX]; } MGraph; void InitGraph(MGraph *G); void Dijkstra(MGraph *G, int start, int dist[], int prev[]); void PrintPath(int start, int end, int prev[]); void ListAllBuildings(MGraph *G); void QueryShortestPath(MGraph *G); void SortEdgesByWeight(MGraph *G); void DFS_Traverse(MGraph *G); #endif头文件里必须写#ifndef防重复包含。这个习惯在课设里体现不出来因为你的代码就三四个文件但老师看代码时会注意到。你可以在答辩时说我用条件编译宏防止头文件被重复包含这是大型项目里必须养成的习惯。这句话虽然简单但能瞬间把你和那些所有代码怼在一个文件里的同学区分开。queryShortestPath 是连接 Dijkstra 和交互界面的枢纽逻辑是输入两个编号 - 检查合法性 - 调 Dijkstra - 输出路径。这里检查合法性必须在调用算法之前做不能等 Dijkstra 算完再检查因为 Dijkstra 遇到非法编号就是数组越界。void QueryShortestPath(MGraph *G) { int start, end; int dist[MAXVEX], prev[MAXVEX]; printf(请输入起点建筑编号); scanf(%d, start); printf(请输入终点建筑编号); scanf(%d, end); if (start 0 || start G-numVertexes || end 0 || end G-numVertexes) { printf(编号超出范围请重新输入\n); return; } if (start end) { printf(起点和终点相同距离为 0。\n); return; } Dijkstra(G, start, dist, prev); if (dist[end] INF) { printf(两栋建筑之间不存在可达路径。\n); } else { printf(最短路径长度%d 米\n, dist[end]); printf(路径); PrintPath(start, end, prev); } }注意里面两个特殊分支起点等于终点的处理以及路径不存在时的处理。这两个分支对应的就是需求分析时写的“边界情况”。你不写这两个分支程序一般也不会崩但输出会非常难看——起点终点相同时路径打印可能输出一个空串或一个孤立节点老师会问“你这算对吗”。你提前处理好了演示时就可以主动输入这两个边界情况展示程序的健壮性这是加分的。4.5 初始化后的第一轮自测功能全绿再往下走代码写到能编译通过别急着去找验收老师先自己当一遍“验收老师”。按照下面的清单过一遍每一条都要真的跑一遍看输出正常路径查询选一条明显绕远的路看程序算出来的最短路径是不是你的手算结果。起点终点相同输出“距离为 0”没有多余路径输出。不存在的编号输入 -1 或 99程序输出提示语不崩溃。不连通节点对如果数据文件里有孤立的建筑查询它和其他建筑时输出“不存在可达路径”。菜单输入错误输入 8 或者 abc程序回到菜单而不是陷入死循环。图数据文件缺失删掉 graph_data.txt 后运行程序看程序是否给了明确提示。第 5 条有个隐藏坑如果输入的是字母而不是数字scanf 会返回 0但变量里保留的是旧值或者未初始化的值程序可能进入死循环。简单的处理方式是检查 scanf 的返回值如果等于 0 就清空输入缓冲并提示重新输入if (scanf(%d, choice) ! 1) { printf(请输入数字\n); while (getchar() ! \n); // 把错误输入全部清掉 continue; }这一步检查在课设里尤其重要。演示时你紧张了、手误了输入了一个字母程序直接卡死你对着黑窗口不知所措——这种场景每年验收都在上演你提前把防护做好就不至于翻车。5. 避坑清单验收前最容易翻车的 5 个位置5.1 编译环境差异Windows 下能跑换台机器就崩现象在自己电脑上编译运行一切正常拿到实验室或答辩机器上编译报一堆错误或者运行时闪退。原因课设最常见的环境是 Dev-C但它的 MinGW 编译器版本可能比你在 VS Code 里用的 GCC 旧对 C 标准支持不到位。比如你在代码里用了for (int i 0; ...)在 C89 标准下就不允许在 for 循环里声明变量。还有//注释之外如果你混用了 C 和 C 代码比如用new或// 单行注释严格模式下编译直接卡死。解决写代码时统一用 C 语言语法变量声明一律放在语句块开头换行注释没问题但不要用 C 的特性。另外提交前最后用 Dev-C 打开项目重新编译一次确认没有警告。警告里经常藏着大坑比如“隐式声明函数”——那就是说你调了一个函数但没包含它的头文件你自己的编译器可能因为自动链接或旧 libc 而放过但答辩机器上就会链接失败。5.2 scanf 输入残留菜单第二次输入直接跳过现象输入菜单选项后按回车程序不会等待输入下一次选项直接“吃掉”了残留的换行符菜单飞速跳转。原因scanf(%d)只读取数字把输入缓冲区里的换行符留下。下一次循环里如果碰到scanf(%c)或gets()会立即读到这个换行符导致输入跳过。解决判断输入是数字还是字符后用while (getchar() ! \n)清空缓冲或者在每个输入语句后统一加一行fflush(stdin)。但注意——fflush(stdin) 在 C 标准里是未定义行为在 Windows 下的 Dev-C 里能用在 Linux 的 GCC 下没用。所以我建议用 getchar 清缓冲的写法兼容性更好也体现你知道输入缓冲区的运作方式答辩时可以主动讲这个细节。5.3 无穷大 INF 参与运算Dijkstra 松弛时整数溢出现象图里有不连通的顶点程序输出的最短路径长度是一个莫名其妙的负数或者路径序列里出现连续的 -1。原因INF 用 65535 定义时如果一行里两个 INF 相加——比如min G-edges[k][j]两个都是 INF——结果超过 int 上限的溢出是小事更常见的是 65535 65535 131070没有溢出但 result 是 131070然后你把 131070 和另一个 INF65535 比较131070 不小于 INF所以不更新——这没错。但如果前面 min 被算成了 32767 之类的更大值或者 INF 定义成0x7fffffff那两个 INF 相加就直接变成负数负数是小于正数 INF 的于是松弛条件永远成立dist 被反复更新成负数输出彻底乱了。解决在松弛操作前加判断两个值都必须小于 INF 才做加法比较if (!final[j] min G-edges[k][j] min) // 这行有问题正确写法是if (!final[j] G-edges[k][j] ! INF min G-edges[k][j] dist[j]) { dist[j] min G-edges[k][j]; prev[j] k; }先把 INF 的边排除掉再做加法。这是一个典型的防御性编程问题代码里处理不好一旦图里有一个不连通点你的 Dijkstra 输出就全错这种 bug 往往到验收前才被发现改起来牵一发动全身血泪教训。5.4 DFS 递归深度图的顶点多了一层就可能崩现象在图的遍历功能里用递归实现 DFS图有 15 个顶点时正常你为了演示加到 20 个顶点程序在遍历到深处时突然崩溃退出。原因递归函数DFS(int v)每次调用都在栈上分配新的栈帧图的深度太大时堆栈溢出。课设的顶点数一般不会上万但运行时栈大小只有 1MB 左右如果递归层数上百层就可能爆栈。解决把 DFS 改成显式栈实现。用一个辅助数组标记访问状态配一个栈来模拟系统调用栈。void DFS_Traverse(MGraph *G) { int visited[MAXVEX] {0}; int stack[MAXVEX], top 0; int v, i; printf(DFS 遍历结果); for (v 0; v G-numVertexes; v) { if (!visited[v]) { visited[v] 1; printf( %s, G-vexs[v].name); stack[top] v; while (top 0) { int cur stack[--top]; for (i 0; i G-numVertexes; i) { if (G-edges[cur][i] ! INF !visited[i]) { visited[i] 1; printf( %s, G-vexs[i].name); stack[top] i; } } } } } printf(\n); }这里我用的是“右入栈、先访问标记再入栈”的写法避免了重复打印问题。实际上这段代码的遍历输出顺序和递归版 DFS 会略有差异但课设里没有任何人关心输出顺序是和递归版一模一样还是略有偏差只要“每个顶点都被访问且输出正确”就没问题。这趟换栈的收益是即使顶点数几百个也绝对不崩你答辩时可以说“这里为了避免递归栈溢出我用了显式栈实现 DFS”这一句话就把整段代码的分量抬起来了。5.5 报告里的运行截图截图不清晰是最亏的丢分点现象课程设计报告里贴了运行截图但截图分辨率过低或者窗口被拉伸老师看不清输出的文字内容只能看到模糊的色块。原因屏幕缩放比例太高或者截图时只截了窗口的一小块报告排版时又把图片拉大了字就变成了“马赛克”。解决截图时用 WinShiftS 选择窗口区域不要把整个屏幕截进来截图后用画图工具把图片裁剪到只留窗口黑色区域保证截图里字体大小用默认的 16 号以上不要为了多放内容把窗口缩得很小。另外每张截图下方要有一行说明文字“图 3-1 最短路径查询结果”这对应报告里的“运行结果与分析”章节。文字说明里要写清楚输入了什么数据、看到了什么输出、结果是否符合预期。这一条不是代码问题但很多代码写得好的人在这里翻车报告分上不去很可惜。6. 答辩与验收从“代码能跑”到“老师点头”的最后一公里到了验收环节代码已经没得改了。这时候拼的是两样东西你怎么讲以及你怎么应对提问。我这里说三个我最常用的答辩技巧都是实战里验证过有用的。第一个是“演示脚本”。不要到了现场凭感觉操作提前写好一个三分钟的演示流程启动程序顺手点开数据文件说明“这是图的数据文件共 6 个顶点 6 条边”然后查询从“图书馆”到“学生食堂”的最短路径故意先说一句“如果走第二教学楼会更近但实际最短路径是先到第一教学楼”——这句话的作用是让老师知道你理解“最短”的含义你不是只会跑程序。然后展示按距离排序和 DFS 遍历最后演示输入非法编号程序提示错误并返回菜单。三分钟结束每个功能都被验证过而且不会冷场。第二个是“高频问题预案”。按我的经验老师最爱问的问题固定就那么几个你提前把答案准备好背熟现场就不卡壳。问Dijkstra 算法为什么不能处理负权边答因为 Dijkstra 基于贪心每次确定一个距离最小的顶点就不再更新。如果存在负权边某个顶点可能在确定之后通过负权边被更短地发现但算法已经不会回头更新它了。所以负权图得用 SPFA 或 Bellman-Ford。问你的图是无向的如果改成有向图代码哪里要改答读取边时只赋一个方向的权值G-edges[v1][v2] weight去掉回赋的那一行。另外 DFS 遍历时也要按有向边来访问邻接点。问为什么邻接矩阵的 INF 用 65535 而不是 32767答65535 是 2 的 16 次方减 1在 int 范围内而且足够大任何两个 INF 相加也不会溢出 int6553565535131070远小于 2^31确保松弛比较时不被意外“污染”。这三个问题你答得顺老师对你的印象分就会往上走。最怕的是背了答案但没理解老师追问一层就露馅。所以预案里的答案一定要自己先弄懂别硬背。第三个是“报告与演示的一致性”。你课程设计报告里写的是什么数据结构、什么算法流程演示时就必须是什么。有些同学报告里写“本系统采用邻接表存储图结构”但代码里用的是邻接矩阵——这种情况老师一旦发现印象分直接清零。报告和代码必须对口。我一般建议在报告里专门画一张“系统功能框图”——但这个不用画得很复杂用方框和箭头把功能模块和数据结构之间的关系标示出来即可。最后一个技巧是关于“求帮助”的。如果现场真的遇到突发状况程序起不来——这不是你的错但别愣着。你可说“我先把数据文件路径检查一下”然后打开当前目录确认文件在不在。如果还是不行就诚恳一点“正常情况下这个功能是好的可能是当前环境的字符编码问题导致文件没有正确读取。” 说实话这个责任其实不在你——你答辩前没在答辩机器上跑过一遍谁知道这机器少了什么运行库。所以我的习惯是答辩前一天借一台跟答辩环境最接近的电脑装上相同的编译器完整跑一遍演示流程。这个动作能排掉 80% 的“机器差异”导致的尴尬。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

IEC104 Client Simulator深度解析:协议调试与主站行为解剖 2026/9/25 4:53:45

IEC104 Client Simulator深度解析:协议调试与主站行为解剖

1. 这不是“点开即用”的玩具,而是一把能捅穿电力监控系统底层逻辑的螺丝刀IEC104 Client Simulator——光看名字,很多人第一反应是“又一个协议测试小工具”,点开下载、双击运行、填几个IP端口就完事。但我在某省调自动化处驻场调试那会儿&a…

阅读更多 →
Botty图形调试器实战:F10模式下蓝圈、红圈与0.9评分到底怎么看? 2026/9/25 4:53:45

Botty图形调试器实战:F10模式下蓝圈、红圈与0.9评分到底怎么看?

Botty图形调试器实战:F10模式下蓝圈、红圈与0.9评分到底怎么看? 【免费下载链接】botty D2R Pixel Bot 项目地址: https://gitcode.com/gh_mirrors/bo/botty Botty 是一款面向《暗黑破坏神2:重制版》(D2R) 的开源像素机器人&#xff0…

阅读更多 →
RT-Thread Smart 在泰山派 RK3566 开发板上的移植与实战:RK3500 BSP 编译、U-Boot 引导与设备树调试全解 2026/9/25 4:53:39

RT-Thread Smart 在泰山派 RK3566 开发板上的移植与实战:RK3500 BSP 编译、U-Boot 引导与设备树调试全解

操作系统嵌入式物联网嵌入式OSRTOS 【免费下载链接】rt-thread RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/ 项目地址: https://gitcode.com/gh_mirrors/rt/rt-thread 点击查看 免费下载 本文以 …

阅读更多 →
Chrome WebGL支持全解析:从检测到开启再到排错的完整指南 2026/9/25 4:53:32

Chrome WebGL支持全解析:从检测到开启再到排错的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Tessent环境OCC选型指南:标准/同步/mini OCC如何抉择 2026/9/25 4:53:32

Tessent环境OCC选型指南:标准/同步/mini OCC如何抉择

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
芯片过温保护逻辑切换:从硬件关断到系统级热管理 2026/9/25 4:53:32

芯片过温保护逻辑切换:从硬件关断到系统级热管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉