[OJ]数据结构:移除链表元素

目录

题目来源:力扣(LeetCode)

一、完整代码

二、解题思路

三、图解

1.初始化

2.循环启动

3.结束循环

四、常见陷阱

1.空指针解引用陷阱

2.内存泄漏陷阱

3.指针丢失陷阱

4.头节点处理陷阱

五、总结


题目来源:力扣(LeetCode)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

一、完整代码

复制代码
struct ListNode* removeElements(struct ListNode* head, int val) 
{
    // 删除头节点为val的情况,直到头节点不是val
    while (head != NULL && head->val == val) 
    {
        struct ListNode* del = head;
        head = head->next;
        free(del);
    }

    // 如果链表为空,直接返回
    if (head == NULL) 
    {
        return NULL;
    }

    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    while (cur) 
    {
        if (cur->val == val) 
        {
            struct ListNode* del = cur;
            prev->next = cur->next;
            cur = cur->next;
            free(del);
        } 
        else 
        {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

二、解题思路

  1. 预处理头节点:先处理所有连续的要删除头节点

  2. 双指针遍历 :
    prev:指向当前节点的前一个节点(初始为NULL);
    cur:指向当前正在检查的节点。

  3. 删除逻辑 :
    当cur->val == val时,将prev->next指向cur->next,定义临时指针del指向cur,然后删除del;
    否则,将prev移动到cur,cur移动到下一个节点。

三、图解

让我们通过一个具体例子来详细理解算法的执行过程。假设我们有以下链表:

要删除所有值为 2 的节点。

1.初始化

  • cur 指向头节点 1

  • prev 初始为 NULL

2.循环启动

(1)第一个循环

由于 cur->val = 1 不等于 2,执行 else 分支:

  • prev = cur(prev指向节点1)

  • cur = cur->next(cur指向节点2)

(2)删除第一个值为2的节点

cur->val = 2 等于要删除的值,执行删除操作:

  • struct ListNode* del = cur(del指向要删除的节点2)
  • prev->next = cur->next(节点1的next指向节点3)
  • cur = cur->next(cur指向节点3)
  • free(del)(释放节点2的内存)

(3)继续遍历,直到遇到val = 2 的节点

.....................

(4)删除第二个值为2的节点

cur->val = 2 等于要删除的值,执行删除操作:

  • struct ListNode* del = cur(del指向要删除的节点2)

  • prev->next = cur->next(节点4的next指向下一个节点2)

  • cur = cur->next(cur指向下一个节点2)

  • free(del)(释放节点2的内存)

(5)删除第三个值为2的节点

cur->val = 2 等于要删除的值,执行删除操作:

  • struct ListNode* del = cur(del指向要删除的节点2)

  • prev->next = cur->next(节点4的next指向节点5)

  • cur = cur->next(cur指向节点5)

  • free(del)(释放节点2的内存)

(6)继续遍历

cur->val = 5 不等于2,执行else分支:

  • prev = cur(prev指向节点5)

  • cur = cur->next(cur指向NULL)

3.结束循环

当 cur = NULL 时,while 循环条件 cur 为假,循环结束。

最终链表为:

返回 head,即节点1

四、常见陷阱

1.空指针解引用陷阱

复制代码
// 错误示例
if (cur->val == val) 
{
    prev->next = cur->next;  // 当prev为NULL时会崩溃
    free(cur);
}

2.内存泄漏陷阱

复制代码
// 错误示例
if (cur->val == val) 
{
    prev->next = cur->next;
    cur = cur->next;
    // 忘记释放del节点的内存!
}

3.指针丢失陷阱

复制代码
// 错误示例
if (cur->val == val) 
{
    free(cur);           // 先释放内存
    prev->next = cur->next;  // 访问已释放内存!
    cur = prev->next;
}

4.头节点处理陷阱

复制代码
// 错误示例
struct ListNode* cur = head;
struct ListNode* prev = NULL;

while (cur) 
{
    if (cur->val == val) 
    {
        if (prev == NULL) 
        {
            head = cur->next;  // 头节点删除
        } 

        else 
        {
            prev->next = cur->next;
        }
        free(cur);
        // 问题:删除头节点后,cur指针状态可能不对
    }
}

五、总结

双指针法是删除链表节点的核心技巧,使用prev和cur指针遍历链表。需要单独处理可能被连续删除的头节点。删除节点时须严格按顺序操作:先保存待删节点,再移动cur指针,接着更新prev->next,最后释放内存。特别注意,连续删除时prev保持不动,只在保留节点时才向后移动。复杂度:时间O(n),空间O(1)。

相关推荐
吞下星星的少年·-·4 小时前
Div.2 CF 1130B题(构造,贪心)
算法·贪心·构造
是main不是漫5 小时前
【双指针——移动零】数组划分类题目的入门基石
c++·算法
·present·5 小时前
CSAPP(Bomb lab)
c语言·c++
深圳市快瞳科技有限公司6 小时前
野外鸟类AI识别落地实录:输入适配、结构化输出、去重聚合与置信度分流的工程实践
算法·计算机视觉·鸟类识别·鸟类ai识别·野外鸟类观测·林业调查
专注仿真8 小时前
梯度提升类(XFBoost)算法详解与代码实例
开发语言·人工智能·python·算法·大模型训练·控制算法
小豆庄8 小时前
视频遮罩追踪退场后ID撞号的排查—多目标场景下的失效
opencv·算法·bug·视频·python3.11
Tisfy8 小时前
LeetCode 3882.网格图中最小异或路径:动态规划
算法·leetcode·动态规划
D e re k ZH9 小时前
P5025 [SNOI2017] 炸弹
c++·算法
A boy CDEF girl9 小时前
算法题专栏:优选算法
数据结构·算法·leetcode
没落之王9 小时前
三易五行看宋朝的三界确立
大数据·人工智能·算法