PTA(链表)1-4 删除链表中的元素
PTA(链表)1-4 删除链表中的元素
zhangzhang1-4 删除链表中的元素
分数 7
作者 李廷元
单位 中国民用航空飞行学院
本题要求删除链表中等于给定值val的所有节点。链表ListNode的定义已经给出。要求给出函数removeElements的实现。
函数接口定义:
1 | /* |
裁判测试程序样例:
1 |
|
输入样例1:
1 | 81 70 49 70 88 84 51 65 60 59 |
输出样例1:
1 | 81 49 88 84 51 65 60 59 |
输入样例2:
1 | 1 |
输出样例2:
1 | NULL |
答案
1 | struct ListNode* removeElements(struct ListNode* head, int val) { |
采用 “分阶段处理” 策略,分两步删除目标节点:
处理头节点
由于头节点没有前驱节点,删除头节点需要特殊处理:
用
while循环连续删除值为val的头节点(应对[2,2,2]删除2这类连续头节点场景)。每次删除后,头指针
head后移(head = head->next)。循环条件
1
head != NULL && head->val == val
确保:
- 不会对空链表访问
head->val(避免空指针错误)。 - 只要头节点值为
val就持续删除。
- 不会对空链表访问
处理非头节点
对于中间 / 尾部节点,通过前驱节点
p进行删除:- 用
p遍历链表,始终检查p->next(下一个节点)是否需要删除。 - 若
p->next->val == val,则跳过该节点(p->next = p->next->next)。 - 若不需要删除,
p正常后移(p = p->next)。
- 用
易错点分析
- 空指针访问风险
- 错误场景:如果链表为空(
head == NULL),直接访问head->val会导致崩溃。 - 代码防护:开头的
if (head == NULL) return head和while循环的head != NULL条件,避免了空指针访问。
- 错误场景:如果链表为空(
- 连续头节点删除不彻底
- 错误场景:若用
if而非while处理头节点(如[1,1,2]删除1),只能删除第一个头节点,残留第二个1。 - 代码防护:
while循环确保所有连续的头节点都被删除。
- 错误场景:若用
- 删除后链表为空的处理
- 错误场景:若删除所有节点后链表为空(如
[1,1]删除1),后续操作p = head会导致p为空,访问p->next出错。 - 代码防护:头节点处理后,再次检查
if (head == NULL) return head,避免后续逻辑出错。
- 错误场景:若删除所有节点后链表为空(如
- 遍历终止条件错误
- 错误场景:若用
p != NULL作为循环条件(而非p->next != NULL),当p指向最后一个节点时,p->next为NULL,访问p->next->val会出错。 - 代码防护:
while (p->next != NULL)确保只检查到倒数第二个节点,避免访问NULL->val。
- 错误场景:若用
- 内存泄漏(隐性问题)
- 代码缺陷:删除节点时未释放内存(
free),虽然不影响编译运行,但长期会导致内存泄漏(尤其在频繁操作的场景中)。 - 优化建议:删除节点前用临时指针保存,删除后释放(如
struct ListNode* temp = head; head = head->next; free(temp);)。
- 代码缺陷:删除节点时未释放内存(
这题没有头结点,会复杂
有头结点时,head 始终指向这个空的哨兵节点,所有数据节点都在 head->next 之后。删除逻辑统一为 “通过前驱节点删除目标节点”,无需分 “头节点 / 非头节点” 处理
无需单独处理原头节点
原头节点(第一个数据节点)变成了
head->next,和其他数据节点一样有前驱(哨兵节点)。删除时直接通过p->next判断,不用写while循环单独处理连续的原头节点。避免空指针访问风险
哨兵节点始终存在(
head不会为空),无需在开头判断 “链表是否为空”,也不用在删除后检查 “链表是否变空”—— 即使所有数据节点都被删除,head仍指向哨兵节点,head->next自然为NULL。逻辑统一,代码更短
所有删除操作都遵循 “找前驱→跳节点” 的统一逻辑,没有分支判断,可读性和维护性更强。


