
简介华南农业大学《数据结构》上机实验指导书附答案是面向高校计算机专业学生的实验教学文档适合正在学习数据结构课程、准备上机考核或复习备考的读者使用。文档覆盖线性表、堆栈、队列、模式匹配、二叉树等核心知识点每个实验按“实验目的—实验内容—实验报告”结构展开既给出题目要求也提供可核对的参考答案。资源包内共1个doc文件大小约639KB内容紧凑集中可直接打开阅读或打印。目前已有293人学习下载是广受同类课程学习者认可的实用资料。通过完成这些实验读者可掌握线性表、栈、队列的数组与链表实现方式理解模式匹配中暴力算法与KMP算法的区别并进一步熟悉二叉树的遍历与基本操作从而为后续算法设计与数据结构进阶奠定扎实基础。1. 数据结构实验指导书这份八件套文件到底能帮你拿到什么这份华南农业大学的数据结构上机实验指导书附答案不是普通课件而是一份从实验一到实验八全部覆盖、附带完整可运行代码和模拟试卷的“数据结构上机全流程包”。很多人以为它是给学生交作业用的模板实际上它最大的价值在于每道题都给出了可运行的 C 代码、测试样例和输出格式相当于把教材里“算法 2.3、2.4、2.5”的抽象描述直接变成了能跑通的东西。如果你正在准备数据结构实验报告、考研复试上机或者想把手写伪代码变成真正的 C 语言程序这份文档能省下大量查错时间。下面我从一个实际拆过这份文档、照着跑过代码的角度把它怎么用、坑在哪、哪些地方需要自己补全说清楚。2. 线性表顺序存储把“补全代码”变成真正能跑的初始化、插入、删除2.1 顺序表结构定义和 InitList_Sqmalloc 不是交了就行文档里的存储结构定义是经典的《数据结构C 语言版》风格SqList里三个字段elem是存储空间基址length是当前长度listsize是当前分配容量。题目 1 的框架代码里留了三个空让你补InitList_Sq、Load_Sq、ListInsert_Sq和ListDelete_Sq。文档后面给了完整代码但如果你直接抄可能会漏掉一个关键细节——InitList_Sq里 malloc 完之后没有判断返回值。int InitList_Sq(SqList L){ L.elem(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType)); if(!L.elem){ printf(Memory allocation failed!\n); return ERROR; } L.length0; L.listsizeLIST_INIT_SIZE; return OK; }这段代码的逻辑是先按LIST_INIT_SIZE100个ElemType大小分配内存L.length置 0L.listsize记录当前容量。参数L是 C 引用语法在纯 C 环境下要改成指针SqList *L并做一次解引用。补全时最容易漏的是 malloc 之后没判空后面ListInsert_Sq扩容时如果 realloc 失败L.elem会变成野指针程序直接段错误。我一般会在这里加一层保护宁可多写两行也不让空指针裸奔。2.2 ListInsert_Sq 和 ListDelete_Sq位置合法性判断和移位方向插入操作是顺序表里最需要小心的函数文档特意把“i 的合法值为 1≤i≤L.length1”写在注释里。完整代码里有两个容易写反的地方一是容量不足时用 realloc 扩容LISTINCREMENT10个元素二是从表尾往表头方向循环移位。int ListInsert_Sq(SqList L,int i,int e){ if(i1||iL.length1) return ERROR; ElemType *newbase,*q,*p; if(L.lengthL.listsize){ newbase(ElemType*)realloc(L.elem,(L.listsizeLISTINCREMENT)*sizeof(ElemType)); if(!newbase) return ERROR; L.elemnewbase; L.listsizeLISTINCREMENT; } q(L.elem[i-1]); // q 指向插入位置 for(p(L.elem[L.length-1]);pq;--p) *(p1)*p; // 从最后一个元素开始后移 *qe; L.length; return OK; }注意这里循环是从L.length-1开始往前移到q的位置pq这个条件保证了插入位置及其后面的元素都后移一位。如果写成从q开始往后移会把后面的元素覆盖掉这是顺序表插入最常见的翻车点。删除操作则相反从删除位置的后一个元素开始往前覆盖循环条件for(p;pq;p) *(p-1)*p;p初始指向删除位置i-1q指向最后一个元素。补全代码时先判断i1||iL.length再操作否则越界访问会读到未初始化内存。2.3 题目 2 的 MergeList 和题目 3 的逆置不提供代码时怎么独立完成题目 2 要求把两个非递减有序顺序表 A、B 合并成非递减的 C文档只给了测试样例不给代码。完整代码在算法思路上用的是双指针归并i和j分别指向 A、B 当前元素k记录 C 的长度。GetElem(La,i,ai)取出 A 的第 i 个元素GetElem(Lb,j,bj)取出 B 的第 j 个元素谁小谁先进 C然后移动对应指针。while((iLa_len)(jLb_len)){ GetElem(La,i,ai); GetElem(Lb,j,bj); if(aibj){ ListInsert_Sq(Lc,k,ai); i; }else{ ListInsert_Sq(Lc,k,bj); j; } } while(iLa_len){ GetElem(La,i,ai); ListInsert_Sq(Lc,k,ai); } while(jLb_len){ GetElem(Lb,j,bj); ListInsert_Sq(Lc,k,bj); }归并结束后两个while循环负责把剩余元素追加进去。这里有个细节k先自增再作为插入位置因为 C 表从第 1 个位置开始插入每插一个 k 加 1。题目 3 的逆置要求“仍占用原顺序表的空间”意思是不能申请新表而是原地交换for(i0;iL.length/2;i)交换L.elem[i]和L.elem[L.length-1-i]。我建议逆置函数独立写不要嵌到 main 里这样题目 2 的合并和题目 3 的逆置可以复用Load_Sq输出。3. 链式存储、堆栈与队列链表定义和栈队列实现的对照理解3.1 单链表 LNode 定义头结点到底要不要next 初始化是生死线第二章进入链式存储文档给出的定义是经典的单链表结点typedef struct LNode{ int data; struct LNode *next; }LNode,*LinkList;新建链表时最常犯的错是只 malloc 头结点却忘了把next置成 NULL导致后续遍历判断p!NULL时访问到野指针。我习惯建表时统一走一个InitList_L函数头结点next显式置空后面插入删除都以“带回一个头结点的链表”为前提。文档的题目里要求补全插入、删除、遍历和顺序表的结构几乎一一对应区别只在于链表的插入不需要移动元素而是修改指针。头插法和尾插法的差异也要注意头插法每次都在头结点后面插入插入顺序和输入顺序相反尾插法需要维护一个尾指针r每插一个更新一次。3.2 堆栈和队列的结构差异后进先出和先进先出不是换个名字文档实验二、实验三分别要求实现堆栈和队列。堆栈的数组实现需要top指针入栈push先判满再top后赋值出栈pop先判空再取元素后top--。队列的数组实现则要两个指针front和rear入队rear出队front循环队列还要(rear1)%MAXSIZE判满。很多同学做完实验二直接照抄堆栈逻辑写队列结果队尾队首分不清。这里有一个判断技巧堆栈只需要一个top就能控制两端队列必须两个指针堆栈判满条件是topMAXSIZE-1循环队列判满条件是(rear1)%MAXSIZEfront。文档提到“同学们可扩展考虑循环链表与双链表”实际上队列用循环数组表示时取余运算里那个“牺牲一个存储单元”的设计就是最容易写错的细节。4. 模式匹配暴力算法和 KMP 的 next 数组到底怎么推4.1 暴力匹配两层循环里的两个指针各自什么时候回退实验四的模式匹配文档明确要求同时实现暴力算法和 KMP 算法。暴力匹配的思路是主串 S 从第 i 个位置起与模式串 T 逐个字符比较失败则 i 回退到 i-j1j 回退到 0。下面这段是完整的暴力匹配实现int Index_BF(char S[], char T[], int pos){ int ipos-1, j0; while(S[i]!\0 T[j]!\0){ if(S[i]T[j]){ i; j; }else{ ii-j1; // 主串回退到本轮起始的下一个位置 j0; // 模式串从头开始 } } if(T[j]\0) return i-j; // 匹配成功返回起始下标 else return -1; }如果匹配失败ii-j1的意思是本轮从 i 开始匹配了 j 个字符后失败主串回到本轮最开始的位置加 1。这个式子网上一搜一堆但真正写的时候很多人把i-j1算成i-j或i-j2导致漏匹配。暴力算法的时间复杂度是 O(n*m)主串长 n、模式串长 m最坏情况下每个位置都要比到模式串最后一个字符才失败。4.2 KMP 的 next 数组理解“最长相等前后缀”比背代码更省事KMP 的核心是 next 数组文档里只给了题目要求没给推导步骤。next[j] 的含义是当模式串第 j 个字符失配时j 应该回退到的位置。对于模式串 abaabc手动推一遍j: 0 1 2 3 4 5 T[j]: a b a a b c next: -1 0 0 1 1 2next[0]-1 是特殊约定的哨兵。next[1]0因为前缀 a 没有相等前后缀。next[2]0因为 ab 的前缀 a 和后缀 b 不相等。next[3]1因为 aba 的最长相等前后缀是 a长度为 1。next[4]1因为 abaa 的最长相等前后缀仍是 a。next[5]2因为 abaab 的最长相等前后缀是 ab。求 next 的代码里最关键的是knext[k]这一行void get_next(char T[], int next[]){ int i0, k-1; next[0]-1; while(T[i]!\0){ if(k-1 || T[i]T[k]){ i; k; next[i]k; }else{ knext[k]; } } }knext[k]是在当前字符不相等时把 k 回退到更短的相等前缀位置继续尝试。这一行是 KMP 里最像玄学的地方很多人抄代码时漏了它结果 next 数组全是错的。我建议在纸上先把模式串的每个子串的前后缀列出来再对照代码理解比空看代码高效得多。KMP 的时间复杂度是 O(nm)因为主串指针 i 从不回退只回退模式串的 j。5. 数据结构实验避坑清单五个最容易翻车的代码细节5.1 realloc 失败导致原指针丢失现象程序跑着跑着插入元素时突然崩溃或者Load_Sq输出乱码。原因realloc失败时返回 NULL如果直接写L.elemnewbase原指针就被覆盖了后续free会崩溃。解决先用临时变量接收 realloc 返回值判空后再赋给L.elem。文档题目 1 的完整代码里没有判空自己补全时建议加上。5.2 插入和删除的移位方向写反现象插入后部分元素丢失或者删除后最后一个元素重复出现。原因插入应该从表尾往前移删除应该从删除位置往后往前覆盖。写反了就把还没移位的元素覆盖掉了。解决插入用for(p(L.elem[L.length-1]);pq;--p)删除用for(p;pq;p)先把循环边界条件和指针初始值写在注释里再编码。5.3 scanf 读入位置不合法时输出未初始化的 e现象删除操作输入的位置超出范围时程序输出“The Element 被删除”但数字是一串随机值。原因case 2里先scanf(%d,i)如果ListDelete_Sq返回 ERRORe根本没有被赋值但 printf 仍然打印了它。解决先判断函数返回值成功才打印 e 的值。这是文档测试样例格式说明里没写清楚的边界情况。5.4 合并测试样例中 B 表的输入循环复制粘贴忘了改上限现象题目 2 输入 B 表数据时只读了一半就跳到输出。原因文档完整代码里有一处经典错误B 表输入循环写的是for(i1;ian;i)复制 A 表的循环忘改bn。如果 A 表 5 个元素、B 表 4 个元素B 表只读 4 个就到上面了。解决凡是复制粘贴循环先看变量名和循环上限是否对应。这一步也能帮你判断是否真的理解了线性表结构。5.5 单链表头结点 next 没有初始化为 NULL现象遍历链表时死循环或者段错误。原因malloc 出来的头结点内存是随机的next没有置空遍历时pp-next走到随机地址。解决建表后立刻L-nextNULL;。这道题在文档里没有现成代码却是链表所有操作的前提。6. 二叉树、查找与排序用附录试卷做一轮复习闭环二叉树的实验五、查找的实验六、内部排序的实验七正好是数据结构考试的三座大山。文档 133 页之后有“数据结构课程设计安排”“图算法实验题目”和“团队题目各种排序算法效率分析”再往后是两套模拟试卷。我的建议是别把模拟试卷留到考前一周才看而是每做完一次实验就做一遍对应部分的题。比如做完二叉树就只看试卷里二叉树相关的选择题和算法题做完排序再对照“团队题目各种排序算法效率分析”自己写一遍比较函数。排序实验的核心是“理解稳定性分别是什么决定的”——冒泡排序相等元素不交换所以稳定简单选择排序每次选最小的放到前面相等元素可能被交换所以不稳定快排的 partition 从两端交替扫描时间复杂度平均 O(n log n) 最坏 O(n²)归并排序稳定但需要 O(n) 辅助空间。文档要求对这些排序做效率分析我一般会写一个统一的测试框架随机生成 10000 个整数分别跑冒泡、选择、插入、快排、归并记录比较次数和移动次数这个实验做完对“什么场景选什么排序”的理解会非常深。二叉树部分递归先序、中序、后序遍历代码只有几行但“非递归中序遍历用栈模拟”才是上机常考的点。查找实验里折半查找的前提是有序表二叉排序树的插入和删除则是考研必考的大题。附录 1 的“实验报告与习题”是这份文档被很多人低估的地方。每份实验报告模板里都有“实验结果与分析”栏把测试样例的输出贴进去再写两行“本次实验遇到的问题及解决方法”整份实验报告的质量立刻上一个台阶。附录 2 的“数据结构课程设计完成情况登记表”可以用来做课程设计的进度规划附录 3 的“图的应用”补上了图实验的缺口——Dijkstra 和 Floyd 算法在那两套模拟试卷里都有对应的填空题。从那以后我每次拿到这种实验指导书都会先通读一遍附录里的试卷和报告模板再回头做实验题——这样能清楚知道哪些知识点是老师真正要考的。做实验五二叉树时我会先写递归遍历验证逻辑再写非递归版本对照输出做实验七排序时我会把所有排序算法写进同一个文件里用宏定义开关切换这样调一个 bug 不用重新编译整个程序。这份指导书的完整代码里有几处隐藏错误比如 B 表输入循环的上限你按“发现问题 - 定位原因 - 修正测试”的顺序走一遍收获比直接抄答案大得多。希望帮到你。本文还有配套的精品资源点击获取