PTA-学校-数据结构(递增的整数序列链表的插入)
1-3 递增的整数序列链表的插入
分数 4
作者 DS课程组
单位 浙江大学
本题要求实现一个函数,在递增的整数序列链表(带头结点)中插入一个新整数,并保持该序列的有序性。
函数接口定义:
List Insert( List L, ElementType X ); |
其中List结构定义如下:
typedef struct Node *PtrToNode; |
L是给定的带头结点的单链表,其结点存储的数据是递增有序的;函数Insert要将X插入L,并保持该序列的有序性,返回插入后的链表头指针。
裁判测试程序样例:
|
输入样例:
5 |
输出样例:
1 2 3 4 5 6 |
作者的第一次代码
List Insert( List L, ElementType X ) { |

你的代码超时的核心原因是存在无限循环,具体问题出在遍历链表的逻辑上。我们来逐步分析并修正:
一、超时原因分析
在你的代码中,第三个分支的遍历逻辑存在严重问题:
PtrToNode prt = L; |
- 问题点:每次进入
while循环,都会执行prt = L->Next,强制将prt重置为链表的第一个元素节点(而不是从上次的位置继续向后移动)。 - 后果:无论循环多少次,
prt始终从第一个元素开始,永远无法遍历到链表的中间或尾部,导致while (prt->Next != NULL)成为无限循环,最终超时。
二、正确的遍历逻辑
插入的核心是找到第一个数据大于等于 X 的节点的前一个位置,然后将 X 插入到该位置之后。具体步骤:
- 从头结点
L开始遍历(利用prt指针),prt始终指向 “当前待比较节点的前一个节点”。 - 当
prt->Next不为空且prt->Next->Data < X时,继续后移prt(prt = prt->Next)。 - 当循环结束时,
prt的位置就是 X 需要插入的前一个位置(此时要么prt->Next为空,要么prt->Next->Data >= X)。
三、修正后的代码
List Insert(List L, ElementType X) { |
四、修正说明
- 统一处理所有情况:无需单独判断 “链表为空”“插入到头部”“插入到尾部” 等情况,上述逻辑可覆盖所有场景:
- 若链表为空(
prt->Next == NULL),循环不执行,直接插入到prt(头结点)后面。 - 若插入到头部(第一个元素就 >=X),循环不执行,插入到 head 后面。
- 若插入到中间或尾部,循环会找到正确的
prt位置。
- 若链表为空(
- 避免无限循环:
prt从L(头结点)开始,通过prt = prt->Next向后移动,每次循环都能推进遍历,不会重置,确保循环能正常结束。 - 简洁高效:时间复杂度为
O(n)(n为链表长度),只需遍历一次找到插入位置,符合题目要求。
通过以上修正,代码可正确处理所有插入场景,且不会出现超时问题。
解析
/* 你的代码将被嵌在这里 */ |
注意
你的代码这次终于解决了核心逻辑错误,能正确处理所有插入场景(头部、中间、尾部、空链表),但仍存在代码冗余问题。下面从「正确性验证」「优点」「优化建议」三部分详细分析:
一、正确性验证(所有场景均通过)
你的代码已能覆盖所有插入需求,我们用 4 个典型场景测试,结果均正确:
| 测试场景 | 原链表 | 插入 X | 代码执行逻辑 | 结果(正确) |
|---|---|---|---|---|
| 1. 空链表 | 头结点→NULL | 5 | 触发L->Next == NULL,创建 p 插入到头结点后,链表变为「头→5→NULL」 |
5 |
| 2. 插入到头部 | 头→2→4→6 | 1 | 触发L->Next->Data(2)>=1,创建 p 插入到头→2 之间,链表变为「头→1→2→4→6」 |
1 2 4 6 |
| 3. 插入到中间 | 头→1→3→5 | 4 | 循环中 prt=3(<4)→prt=5(prt->Next=NULL,循环退出)→prt->Data (5)>=4,创建 p4 插入到 prt2 (3) 后面,链表变为「头→1→3→4→5」 | 1 3 4 5 |
| 4. 插入到尾部 | 头→2→3→7 | 8 | 循环退出后 prt=7(<8),创建 p3 插入到 7 后面,链表变为「头→2→3→7→8」 | 2 3 7 8 |
核心修正:将最后一段插入逻辑的prt->Next = p4改为prt2->Next = p4,解决了 “插入到最后一个节点之前” 时的顺序错误,这是关键进步。
二、代码的优点
- 双指针同步跟踪正确
prt(当前遍历节点)和prt2(当前节点的前一个节点)在循环外初始化,且同步后移(prt=prt->Next; prt2=prt2->Next),确保中间插入时能准确定位 “前一个节点”,避免了之前的跟踪错位问题。 - 边界防护完整
- 提前判断空链表(
L->Next == NULL),避免后续遍历访问NULL->Next导致崩溃; - 提前判断头部插入(
L->Next->Data >= X),减少不必要的循环,提升效率。
- 提前判断空链表(
三、优化建议(非错误,提升简洁性和可维护性)
你的代码每次插入都单独malloc新节点(p/p2/p3/p4),重复了 “内存分配 + 数据赋值” 的逻辑,可统一创建 1 个新节点,减少冗余代码:
优化后的代码(逻辑不变,更简洁)
List Insert(List L, ElementType X) { |
优化点说明:
- 只
malloc一次newNode,后续仅通过调整指针实现插入,减少重复代码; - 变量名统一(用
newNode代替p/p2/p3/p4),提升代码可读性。
总结
你的代码已完全满足题目要求,能正确、稳定地完成插入操作。优化后的版本在保持逻辑正确性的前提下,减少了冗余,更易维护。
要不要我帮你整理一份包含所有测试场景的代码验证清单?清单会明确每个场景的输入、预期输出和代码执行步骤,方便你后续复习或调试参考。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 好的好的378的博客!
评论