ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多项式加法实验:线性表链表实现与调试全解析

多项式加法实验:线性表链表实现与调试全解析 简介南京邮电大学《数据结构》课程实验一完整实验报告面向正在学习线性表与多项式运算的本科生可帮助理解顺序表和单链表的存储结构及基本算法。报告按实验流程展开含实验目的与要求、开发环境、顺序表及带表头单链表的算法设计、流程图、模块划分、核心C源代码与时间复杂度分析并覆盖一元多项式的创建、输出、撤销、加法与乘法实现其中多项式加法通过遍历链表合并同指数项乘法逐项相乘后再合并同类项完整展示了线性表在算术运算中的应用。内容源于2021/2022学年第一学期真实教学场景代码注释详细结论和测试说明完整适合课程设计参考、复习备考或实验前预习。压缩包共1个docx文档大小444KB阅读排版与格式统一便于直接打开参考。CSDN平台已有779人学习下载是南邮该课程实验报告的高人气参考版本。1. 多项式加法为什么是线性表实验的“照妖镜”数据结构实验一放到开学第五周左右每年都有一批同学在多项式加法上交出“能运行但结果不对”的代码。反直觉的现象是程序崩溃的往往不是加法本身而是前面的录入排序和后面的输出格式——这两处只要有一个指针没按预期走加法的三指针归并就全部白费。这篇笔记围绕线性表基本运算与多项式的算术运算这条主线把顺序表、链表两种存储结构怎么做多项式如何用链表建模并按降幂存储加法归并里节点所有权怎么处理一次讲清楚最后落到排查手段。适合第一次写链表实验、被指针折腾到怀疑人生的同学也适合交报告前想检查实现边界的读者。2. 存储结构选型顺序表与链表的性能边界和实现差异线性表这一层抽象落到 C 语言就是两种长相连续数组和散落节点。实验一里基础运算用哪种都能过但多项式部分基本都会用链表——原因是多项式的项数录入前不可预知加法里又要反复插入、删除、合并顺序表每插入一次就搬动一整段元素。动手写代码前先把这两种结构的边界摸清楚后面调试会省很多时间。2.1 顺序表内存连续带来的插入删除成本顺序表的本质是一个数组加一个 length 计数器。插入在逻辑位置 i从 1 计数时要把 i 及其后的元素全部往后搬一位删除则往前搬。搬移的代价是 O(n)换来的是按下标随机访问的 O(1)。多项式加法里合并同类项、删除零项都发生在表中间每删一项就搬一次项数一多整体就在 O(n²) 里打转。所以顺序表适合“查找多、插入删除少、规模稳定”的场景比如学生信息表而不是多项式。最小可用的顺序表插入和删除实现如下#define MAXSIZE 100 typedef struct { ElemType data[MAXSIZE]; // 数组容量固定 int length; // 当前元素个数不是数组下标上限 } SqList; // 在第 i 个位置插入 ei 从 1 开始数 int ListInsert(SqList *L, int i, ElemType e) { if (i 1 || i L-length 1) return 0; // 边界length1 是允许的 if (L-length MAXSIZE) return 0; // 数组已满 for (int j L-length; j i; j--) { // 从后往前搬避免覆盖 L-data[j] L-data[j - 1]; } L-data[i - 1] e; // 逻辑位置 i 对应下标 i-1 L-length; return 1; } int ListDelete(SqList *L, int i, ElemType *e) { if (i 1 || i L-length) return 0; // 删除位置必须落在已有元素内 *e L-data[i - 1]; // 用输出参数把被删元素带出去 for (int j i; j L-length; j) { L-data[j - 1] L-data[j]; // 从前往后搬填补空位 } L-length--; return 1; }逻辑说明插入时 i 的有效范围是 1 到 length1length1 表示插到表尾这是初学者最容易写错的地方。循环里 j 从 length 递减到 i把 data[j-1] 搬到 data[j]最后一个元素先让位从头往尾搬会互相覆盖。data[i-1] 才是插入位直接用 data[i] 必然越界。删除时 i 最大到 length 而不是 length1因为必须能删到已存在的元素。两个函数统一返回 1/0 表示成功失败这是 C 语言实验里最朴素也最可靠的状态约定。参数说明ListDelete 的第三个参数 e 是指针用来把被删元素“带出来”。为什么不用函数返回值因为返回值要留给状态码这是 C 语言输出参数最常见的形态。实验报告里如果老师要求“删除后打印被删元素”这个参数就是为它准备的。再补一个查找函数它是顺序表的看家本领int LocateElem(SqList L, ElemType e) { for (int i 0; i L.length; i) { if (L.data[i] e) return i 1; // 返回逻辑序号从 1 开始 } return 0; // 0 表示查无此元素 }LocateElem 返回逻辑序号而不是数组下标是为了和 ListInsert、ListDelete 的 i 语义保持一致。返回值 0 代表“不存在”1 到 length 代表位置。这个约定要贯穿整个实验别在查找里突然换成 -1否则主函数的判断逻辑就得两头改。2.2 带头节点链表哨兵节点不是可有可无链表部分我用带头节点的单向链表。头节点不存数据只提供一个 next 指针。它的价值在于空表和在首位置插入时操作逻辑和其他位置完全一致不用写 if (pos 1) 这样的特判。不带头节点的写法需要频繁更新头指针函数签名不可避免要写成二级指针 LinkList *L初学者指针 bug 里十有八九就出在这里。typedef struct Node { ElemType data; struct Node *next; } Node, *LinkList; // 初始化malloc 一个头节点让 L 永远指向存在的节点 void InitList(LinkList *L) { *L (LinkList)malloc(sizeof(Node)); if (*L NULL) exit(1); // 教学实验也要查 malloc 失败 (*L)-next NULL; } // 尾插法追加到链表末尾 void AppendNode(LinkList L, ElemType e) { Node *s (Node *)malloc(sizeof(Node)); if (s NULL) exit(1); s-data e; s-next NULL; Node *p L; // 从头节点开始走不是从首节点 while (p-next ! NULL) { p p-next; } p-next s; }逻辑说明尾插的 p 从 L 开始而不是从 L-next 开始这样空表时 while 一次都不走直接把 s 挂到头节点后面首节点和其他节点没有区别。AppendNode 不需要修改 L 本身只修改头节点的 next 域所以参数是一级指针 LinkList L而 InitList 要修改 L 这个头指针本身必须传 LinkList *L。判断“什么时候用二级指针”的标准就是看函数里有没有给指针变量本身重新赋值的需求。malloc 返回值检查很多教科书示例都偷懒不写但实验数据一旦稍大malloc 失败会让整段程序静默崩溃。浪费一行 if 不丢人实验报告里写上反而是亮点。还有一种常见误用在 InsertList 里每插入一次就 malloc 一次却忘了判断 malloc 失败时空指针也会被解引用。凡是拿到 malloc 返回值后马上访问其成员的操作前面都应该有一道 NULL 防线。2.3 存储结构选型的判断依据与实验适用性顺序表和链表的取舍可以用四个维度量化维度顺序表链表随机访问O(1)按下标直达O(n)必须遍历插入删除需要搬移 O(n) 元素改指针 O(1)查找仍 O(n)空间分配必须预分配 MAXSIZE按需 malloc无容量上限存储密度高无指针开销低每个节点多一个指针多项式运算属于典型的“插入删除频繁、规模未知”场景录入时每来一项就要插到有序位置加法时又要合并和删除节点链表天然合适。基础线性表部分用哪种都行但我的建议是如果你多项式已经用链表基础运算也统一用链表一套代码两种用途实验报告的篇幅和学习成本都更可控。反过来如果你对数组下标更敏感基础运算用顺序表、多项式用链表也完全合法只要在报告里写清楚选型理由即可。选型理由在实验报告里是加分部分。别写“因为链表更高级”要写“多项式项数不确定且需要频繁插入删除顺序表的 O(n) 搬移会随项数增长变成主要开销因此选用链表”。这一句话就把数据结构和算法分析串起来了。另外要注意的是项数只有十几项时顺序表的 O(n) 和链表的 O(n) 差别肉眼根本看不出来所以选链表的真正理由是“规模未知 中间位置操作多”不是性能差距本身——能说清这一点答辩时老师追问也不怕。3. 多项式的链表建模节点设计、有序创建与输出格式多项式部分代码量不大但细节密度极高。节点里存什么、链表是否有序、输出格式怎么处理这三个问题动手前就要定下来否则后面每改一次都是大手术。3.1 节点设计系数用 float 还是 double指数用 int 的边界多项式每一项是“系数指数”的二元组。系数我用 double 而不是 float理由很实际加法里要做系数求和float 只有 7 位有效数字0.1 加 0.2 再减 0.3 会残留 1e-8 级别的误差导致本应为 0 的项变成 -0.000000x^3。double 同样有误差但量级到 1e-16配合容差判断足够稳。指数用 int因为指数只参与比较和输出不会产生小数。typedef struct PolyNode { double coef; // 系数double 避免浮点精度问题 int expn; // 指数非负整数 struct PolyNode *next; } PolyNode, *PolyLink;参数说明coef 是运算焦点所有加减都作用在它上面expn 只用于排序和判断同类项。一个 double 加一个 int节点总大小 16 字节没有浪费。如果你用 int 存系数遇到 2.5 这种输入就要改写整个程序所以实验一开始就用 double 是给自己留后路。指数方面我还习惯默认非负整数负数指数属于非法输入在录入时直接拒绝省得加法里还要分类讨论指数符号。头节点的 coef 和 expn 不参与运算只把 next 指向首项。有些同学想利用头节点存“多项式项数”这在单链表里没必要——遍历一遍就数出来了专门维护一个计数器只会多一份出错机会。如果实验报告里要求统计项数写一个遍历计数的函数比维护头节点字段更稳。3.2 保持降幂有序插入时定位前驱的写法我在录入阶段就维护链表的降幂有序性这样加法可以直接走归并。录入的核心函数是 InsertPoly它同时承担两件事找插入位置、合并同类项。很多同学的版本把这两个逻辑拆开写结果要么漏了合并要么插入位置算错。合成一个函数每个分支只做一件事反而好调试。void InsertPoly(PolyLink L, double coef, int expn) { PolyNode *p L; while (p-next p-next-expn expn) { p p-next; // 找第一个指数 expn 的节点的前驱 } if (p-next p-next-expn expn) { p-next-coef coef; // 已有同指数项直接累加 } else { PolyNode *s (PolyNode *)malloc(sizeof(PolyNode)); if (s NULL) exit(1); s-coef coef; s-expn expn; s-next p-next; p-next s; } }逻辑说明p 从头节点出发只要后继节点的指数比新项大就继续前进循环结束时p-next 要么是 NULL要么指数小于等于新项。如果恰巧等于说明输入里已经存在同指数的项系数累加即可如果不等或 NULL就 malloc 一个新节点挂在 p 后面。这里循环条件用是因为整条链按降幂排方向反了链表就变成升幂后续加法全错。为什么要合并同类项提前到录入时做因为录入时指数可能重复比如先输入 2x^3 再输入 -2x^3拖到加法阶段处理会增加归并分支的复杂度。录入合并之后加法函数只需处理“指数不相等”和“指数相等且系数非零”两种情况这已经是简化后的版本。接下来是读入完整多项式的入口PolyLink CreatePoly() { PolyLink L (PolyLink)malloc(sizeof(PolyNode)); if (L NULL) exit(1); L-next NULL; int n; printf(输入项数: ); scanf(%d, n); for (int i 0; i n; i) { double coef; int expn; printf(第 %d 项(系数 指数): , i 1); scanf(%lf %d, coef, expn); InsertPoly(L, coef, expn); } return L; }CreatePoly 先造头节点然后循环读入 n 个系数指数对逐项 InsertPoly。注意 scanf 的格式串系数是 double 必须用 %lf指数用 %d。写错格式串编译器不报错但运行时 scanf 会读入垃圾值这是 C 语言实验里最隐蔽的一类输入 bug。如果输入的系数是整数比如 3%lf 依然能正确读入不用担心。这里每次插入都是从头开始找位置复杂度是 O(n²)但实验数据项数一般不超过二十体感为零如果你想把复杂度压到 O(n)可以要求输入本身就有序那 InsertPoly 就退化成尾插了。3.3 多项式输出的格式处理系数 1、-1、指数 0、1 的特判输出是整个实验扣分最重的地方。不处理格式的朴素输出长这样1.000000x^3-2.000000x^2-1.000000验收时老师看了血压直接上来。标准输出要求系数为 1 时省略 1系数为 -1 时只留负号指数为 0 时不输出 x^0指数为 1 时不输出 ^1非首项正系数要补加号。这些特判全部堆在 printf 里会很难读我习惯用一个干净的 PrintPolyvoid PrintPoly(PolyLink L) { PolyNode *p L-next; if (!p) { printf(0\n); return; } // 空多项式必须能输出 int first 1; while (p) { double c p-coef; int e p-expn; if (first) { // 首项正系数不带 if (c 1 e ! 0) ; // 系数 1 且含 x 时省略系数 else if (c -1 e ! 0) printf(-); else printf(%.2lf, c); first 0; } else { if (c 0) printf(); else if (c 0) printf(-); if (fabs(c) ! 1 || e 0) // 系数绝对值非 1或没有 x 时 printf(%.2lf, fabs(c)); } if (e ! 0) { // 指数非零才输出 x 部分 printf(x); if (e ! 1) printf(^%d, e); } p p-next; } printf(\n); }逻辑说明整个输出拆成两段先处理符号和系数再处理 x 和指数。首项正系数不输出加号非首项正系数必须补加号系数绝对值等于 1 且后面跟着 x 时系数整体省略只保留符号。指数为 0 时整个 x 段跳过直接输出系数本身。这个拆分让每一类特判只出现一次不会出现“又处理符号又处理 x”的混乱。输出格式的边界条件很多系数是 0 的项应该在加法里已经被删除如果还有残留PrintPoly 会打出 0.00x^2 这类怪东西——这通常说明前面的归并分支漏了零项判断。我在调试时经常调 PrintPoly 看中间结果所以它的健壮性比效率重要得多。很多同学把打印逻辑放在主函数里手写循环结果是每加一个功能就要改一遍主函数。抽出独立函数是这类实验最值得花的几分钟。4. 多项式加法核心归并合并同类项与节点复用问题加法是算术运算的主干。两个按降幂排列的链表相加核心是一次归并同时扫描两条链指数大的先进结果链指数相等的合并系数。这个算法的循环不变量是pc 始终指向 C 的尾节点pa 和 pb 分别指向 A、B 中还没处理的剩余部分的首节点。只要维护好这三个指针整个加法就是一条单向的推进不需要回头。4.1 算法思想三指针归并的循环不变量举个例子。A 是 3x^5 2x^3 - x^2B 是 -2x^3 x 4。指针 pa 指向 3x^5pb 指向 -2x^3。比指数5 大于 3pa 的节点进 Cpa 后移到 2x^3。再比3 等于 3系数 2 加 -2 得 0两个节点都丢弃pa 后移到 -x^2pb 后移到 x。继续比2 大于 1-x^2 进 C……最终 C 是 3x^5 - x^2 x 4。合并产生的 0x^3 项在归并过程中被直接掐掉。这个例子的关键在指数相等且系数为 0 的分支不能把系数为 0 的节点链进 C否则输出多项式会多出一个 0x^3看起来是小瑕疵但老师手工推演验收时一眼就能看到。我的建议是系数求和后立刻判断非零才接入 C为零则两个源节点都释放。复杂度方面归并本身是 O(mn)这是有序链表带来的最大红利。如果录入时没有维护有序性加法就得每处理一项在结果链里重新查找插入位置整体变成 O(m×n)项数过二十就明显发飘。实验报告里写上这句复杂度分析是拉开档次的地方。4.2 加法代码实现与节点释放策略代码实现如下我用的策略是复用 A、B 的节点而不是新 malloc结果链 C 直接接管 A、B 中挪过来的节点。代价是加法完成后 A、B 两条链表不能再单独释放必须把所有权全部交给 C。这条约定必须在函数末尾写清楚否则主函数一不小心二次 free程序必崩。PolyLink AddPoly(PolyLink A, PolyLink B) { PolyNode *pa A-next; PolyNode *pb B-next; PolyLink C (PolyLink)malloc(sizeof(PolyNode)); // C 带头节点 if (C NULL) exit(1); PolyNode *pc C; while (pa pb) { if (pa-expn pb-expn) { // A 当前指数大A 的节点进 C pc-next pa; pc pa; pa pa-next; } else if (pa-expn pb-expn) { pc-next pb; pc pb; pb pb-next; } else { // 指数相等合并系数 double sum pa-coef pb-coef; PolyNode *tmp_a pa; // 先保存指针稍后释放 PolyNode *tmp_b pb; pa pa-next; // 先让 pa、pb 脱离旧节点 pb pb-next; if (sum ! 0.0) { tmp_a-coef sum; // 直接把和写进 A 的节点 pc-next tmp_a; pc tmp_a; } else { free(tmp_a); // 系数为 0两个节点都释放 } free(tmp_b); } } pc-next pa ? pa : pb; // 剩余部分整体接入 A-next NULL; // 所有权移交 CA/B 不再持有 B-next NULL; return C; }逻辑说明指数相等分支里先保存 tmp_a 和 tmp_b再把 pa、pb 各自前移最后才决定复用还是释放。顺序不能反——如果先 free 再取 pa-next就踩了悬空指针。sum 非零时把 tmp_a 的系数改成和直接链进 C省一次 mallocsum 为零时两个源节点都释放结果链里不出现零项。循环结束后pa 和 pb 至少有一个是 NULL用pa ? pa : pb把剩余段整段接入这一段本身有序不需要再逐节点处理。参数说明A、B 必须是带头节点、按指数降序排列的链表。函数返回结果链表的头指针 C。调用方的责任是用完 C 后只释放 C 及其所有节点绝不能再碰 A、B 的 next——它们已经被置 NULL。这个“谁 malloc 谁 free所有权一次移交”的约定是 C 语言链表实验最核心的工程习惯。还有一种做法是全程 malloc 复制节点C 链完全独立于 A、B代价是每个节点多一次分配。我一般不用复制方案因为实验数据量小复用省内存也快但如果你对所有权不放心复制方案更安全报告里写清楚“复制而非接管”即可两种方案老师都接受。4.3 减法与乘法的扩展思路多项式的算术运算通常不止加法。减法最简单把 B 的所有系数取反再调 AddPoly(A, B) 即可。void NegPoly(PolyLink B) { PolyNode *p B-next; while (p) { p-coef -p-coef; p p-next; } }乘法稍微绕一点A 的每一项乘以 B 的每一项得到一个“系数指数”对然后逐项做 InsertPoly 到结果链 C 里。InsertPoly 自带“指数相等则累加”的逻辑所以乘积里相同指数的项会自动合并不需要额外写归并PolyLink MulPoly(PolyLink A, PolyLink B) { PolyLink C (PolyLink)malloc(sizeof(PolyNode)); if (C NULL) exit(1); C-next NULL; for (PolyNode *pa A-next; pa; pa pa-next) for (PolyNode *pb B-next; pb; pb pb-next) InsertPoly(C, pa-coef * pb-coef, pa-expn pb-expn); return C; }乘法的时间复杂度是 O(m×n)每次 InsertPoly 又要在结果链上找位置整体偏慢但实验数据量小完全够用。这里最划算的地方是乘法完全复用了录入时写的有序插入逻辑不需要额外的合并代码。如果你在实验报告里写“乘法通过逐项乘积后调用有序插入函数自动合并同类项”老师就知道你是真理解了前面的设计。5. 实验常见问题排查最容易翻车的5个细节这一章把我在调试中真实遇到、以及帮同学排查过的 5 个高频问题按“现象 → 原因 → 解决”列出来。每一类都对应一段真实的踩坑经验验收前照着过一遍能挡掉一大半隐藏 bug。5.1 输出结果里出现 -0.000000x^3现象系数合并后理论值应该是 0打印出来却是 -0.000000x^3 这样一项。原因浮点数精度。0.1 0.2 - 0.3 在 IEEE 754 下不是 0而是约 5e-17double 也一样只是更隐蔽。系数用 float 时误差更明显double 只是把问题缩小到肉眼难察觉并没有消失。解决判断系数是否为 0 时用容差而不是相等比较写成if (fabs(sum) 1e-6)把小于 1e-6 的都视为零并释放节点。实验数据如果是整数系数一般不会触发这个坑一旦输入带小数这个判断就是保命的。输出端的兼容做法是打印时用 %.2lf 保留两位小数把 1e-16 这种尾巴直接截掉。我见过有同学用printf(%g, c)来规避结果零项变成 5.55112e-17比 -0.000000 还难看不推荐。5.2 插入顺序对但打印出来多项式是乱的现象明明调用了 InsertPoly遍历打印时指数忽大忽小排列不成降序。原因InsertPoly 循环里的比较符号写反了。降幂序要求p-next-expn expn才继续走写成后链表会变成一个先降后升的 V 形加法归并完全失去意义。解决打印前先写一个检查函数验证有序性比如每走一步断言前一节点指数大于后一节点指数。我习惯在调试阶段保留这个断言实验验收前再删。用 Debug 编译时也可以直接assert(p-next-expn p-next-next-expn)。这种错误光靠眼睛盯 printf 输出很难发现因为中间几项可能凑巧是对的验证函数一遍就能定位。5.3 释放指针后继续访问程序时好时坏现象加法跑完有时正常有时崩换一组输入就段错误多跑几次甚至同一组输入结果都不一样。原因指数相等分支里free 了 tmp_a 之后又通过旧的 pa 指针读了它。free 释放的内存不一定立刻被改写所以表现出“这次没事、下次崩”的随机性这是悬空指针的典型症状——玄学 bug 里最磨人的一种。解决养成“释放之前先摘指针”的习惯。4.2 节里pa pa-next必须出现在free(tmp_a)之前就是这个道理。任何 free 调用前都要问一句还有没有别的指针指向这块内存如果有先把它们全部挪走。拿 valgrind 跑一遍凡是报 Invalid read 的基本都是这类问题。5.4 主函数里重复初始化把链表指针覆盖了现象程序跑着跑着第一条链表“消失”了遍历它只输出 0 项或直接崩。原因主函数里不小心写了两次 InitList(L)第二次 malloc 出来的头节点覆盖了原来的 L 指针旧链表整体丢失而且那部分内存没有 free泄漏在堆里。解决一个链表只初始化一次。如果确实想重新来过先调用 DestroyList 把旧链表的每个节点 free 掉再 InitList。写一个完整的销毁函数是实验报告里的加分项大多数同学的代码只有创建没有销毁valgrind 一跑就是一堆 definitely lost。5.5 scanf 读入系数和指数时格式串写错现象输入2.5 3程序读到的 coef 不对甚至后面的循环直接乱掉。原因scanf 格式串和变量类型不匹配。比如 coef 是 double却写了%dscanf 把 2.5 的整数部分 2 读进 coef剩下的.5 3全留在输入缓冲区下一次循环 scanf 从.5开始读直接读到垃圾值。编译器完全不检查这一步只能在运行时暴露。解决格式串严格对齐类型scanf(%lf %d, coef, expn)。如果多个 scanf 在一个循环里输入缓冲区的换行符也要注意用scanf( %lf %d, coef, expn)在格式串前加一个空格吃掉残留换行。这是我头几次实验翻车的主因检查顺序永远是先看格式串再看变量类型。提示scanf 的返回值可以用来做输入校验返回值为 2 才说明两个变量都成功读入返回值不是 2 时缓冲区里必然残留坏数据这时候 break 出去比继续读更安全。6. 用 GDB 和分步打印验证你的链表到底有没有断链最后一个技巧也是我每次实验提交前会做的收尾检查对链表做一次“健康体检”。方法有三个从简单到进阶按需选用。6.1 打印型调试与边界用例最笨但最可靠的是打印型调试。写一个 DebugPrintPoly把每个节点的地址、系数、指数和 next 都打出来一眼能看出三类问题地址重复说明链成环了next 太早为 NULL 说明链断了指数顺序混乱说明插入逻辑有错。void DebugPrintPoly(PolyLink L) { PolyNode *p L-next; int cnt 0; while (p) { printf([%p] coef%.2lf expn%d next%p\n, (void *)p, p-coef, p-expn, (void *)p-next); cnt; p p-next; } printf(共 %d 项\n, cnt); }打印型调试的缺点是要人工盯输出但实验数据只有几十项盯一遍不超过一分钟。第二个手段是 GDB 断点观察在pc-next pa;这类关键赋值上打断点用print pa-expn和print pb-expn逐轮确认三个指针都在按预期推进。我调试时最怕的不是逻辑错而是“我以为 pa 走了实际没走”GDB 能直接把这种错觉消灭掉。第三个手段是边界用例组合。我每次写完加法固定跑四个用例空表加空表、空表加单节点多项式、两个完全相同的多项式、两个互为相反数的多项式。这四个用例覆盖了归并循环的每个分支——空循环、单次推进、指数相等非零、指数相等为零——跑通它们基本可以确定没有断链、没有泄漏、零项分支正确。检查完再把调试用的打印函数从主流程里移除代码就干净了。我自己的习惯是实验一提交前在终端跑一次 valgrind命令只有一行valgrind --leak-checkfull ./poly。如果输出里有 definitely lost 或者 Invalid read说明节点释放没写对回到 4.2 节再看所有权交接。如果你还没装 valgrind单靠四个边界用例也能排查大部分问题内存泄漏部分可以靠肉眼数 malloc 和 free 是否配对来兜底。希望这篇笔记能帮你把实验一做扎实——线性表这个地基打牢了后面的树、图、查找排序实验都会省力很多。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进